用Python实现服务器中只重载被修改的进程的方法


Posted in Python onApril 30, 2015

现在,我们已经把一个Web App的框架完全搭建好了,从后端的API到前端的MVVM,流程已经跑通了。

在继续工作前,注意到每次修改Python代码,都必须在命令行先Ctrl-C停止服务器,再重启,改动才能生效。

在开发阶段,每天都要修改、保存几十次代码,每次保存都手动来这么一下非常麻烦,严重地降低了我们的开发效率。有没有办法让服务器检测到代码修改后自动重新加载呢?

Django的开发环境在Debug模式下就可以做到自动重新加载,如果我们编写的服务器也能实现这个功能,就能大大提升开发效率。

可惜的是,Django没把这个功能独立出来,不用Django就享受不到,怎么办?

其实Python本身提供了重新载入模块的功能,但不是所有模块都能被重新载入。另一种思路是检测www目录下的代码改动,一旦有改动,就自动重启服务器。

按照这个思路,我们可以编写一个辅助程序pymonitor.py,让它启动wsgiapp.py,并时刻监控www目录下的代码改动,有改动时,先把当前wsgiapp.py进程杀掉,再重启,就完成了服务器进程的自动重启。

要监控目录文件的变化,我们也无需自己手动定时扫描,Python的第三方库watchdog可以利用操作系统的API来监控目录文件的变化,并发送通知。我们先用easy_install安装:

$ easy_install watchdog

利用watchdog接收文件变化的通知,如果是.py文件,就自动重启wsgiapp.py进程。

利用Python自带的subprocess实现进程的启动和终止,并把输入输出重定向到当前进程的输入输出中:

#!/usr/bin/env python
import os, sys, time, subprocess

from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

def log(s):
  print '[Monitor] %s' % s

class MyFileSystemEventHander(FileSystemEventHandler):
  def __init__(self, fn):
    super(MyFileSystemEventHander, self).__init__()
    self.restart = fn

  def on_any_event(self, event):
    if event.src_path.endswith('.py'):
      log('Python source file changed: %s' % event.src_path)
      self.restart()

command = ['echo', 'ok']
process = None

def kill_process():
  global process
  if process:
    log('Kill process [%s]...' % process.pid)
    process.kill()
    process.wait()
    log('Process ended with code %s.' % process.returncode)
    process = None

def start_process():
  global process, command
  log('Start process %s...' % ' '.join(command))
  process = subprocess.Popen(command, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr)

def restart_process():
  kill_process()
  start_process()

def start_watch(path, callback):
  observer = Observer()
  observer.schedule(MyFileSystemEventHander(restart_process), path, recursive=True)
  observer.start()
  log('Watching directory %s...' % path)
  start_process()
  try:
    while True:
      time.sleep(0.5)
  except KeyboardInterrupt:
    observer.stop()
  observer.join()

if __name__ == '__main__':
  argv = sys.argv[1:]
  if not argv:
    print('Usage: ./pymonitor your-script.py')
    exit(0)
  if argv[0]!='python':
    argv.insert(0, 'python')
  command = argv
  path = os.path.abspath('.')
  start_watch(path, None)

一共50行左右的代码,就实现了Debug模式的自动重新加载。用下面的命令启动服务器:

$ python pymonitor.py wsgiapp.py

或者给pymonitor.py加上可执行权限,启动服务器:

$ ./pymonitor.py wsgiapp.py

在编辑器中打开一个py文件,修改后保存,看看命令行输出,是不是自动重启了服务器:

$ ./pymonitor.py wsgiapp.py 
[Monitor] Watching directory /Users/michael/Github/awesome-python-webapp/www...
[Monitor] Start process python wsgiapp.py...
...
INFO:root:application (/Users/michael/Github/awesome-python-webapp/www) will start at 0.0.0.0:9000...
[Monitor] Python source file changed: /Users/michael/Github/awesome-python-webapp/www/apis.py
[Monitor] Kill process [2747]...
[Monitor] Process ended with code -9.
[Monitor] Start process python wsgiapp.py...
...
INFO:root:application (/Users/michael/Github/awesome-python-webapp/www) will start at 0.0.0.0:9000...
Try
Python 相关文章推荐
python动态参数用法实例分析
May 25 Python
完美解决python3.7 pip升级 拒绝访问问题
Jul 12 Python
Python 保持登录状态进行接口测试的方法示例
Aug 06 Python
python 叠加等边三角形的绘制的实现
Aug 14 Python
Python 分发包中添加额外文件的方法
Aug 16 Python
Python多线程爬取豆瓣影评API接口
Oct 22 Python
Django xadmin开启搜索功能的实现
Nov 15 Python
Python enumerate函数遍历数据对象组合过程解析
Dec 11 Python
python numpy生成等差数列、等比数列的实例
Feb 25 Python
jupyter 导入csv文件方式
Apr 21 Python
详解Python 函数参数的拆解
Sep 02 Python
通过实例解析python subprocess模块原理及用法
Oct 10 Python
python同时给两个收件人发送邮件的方法
Apr 30 #Python
python通过邮件服务器端口发送邮件的方法
Apr 30 #Python
在Python的web框架中中编写日志列表的教程
Apr 30 #Python
python登录pop3邮件服务器接收邮件的方法
Apr 30 #Python
python通过smpt发送邮件的方法
Apr 30 #Python
在Python的web框架中编写创建日志的程序的教程
Apr 30 #Python
用Python实现web端用户登录和注册功能的教程
Apr 30 #Python
You might like
php,不用COM,生成excel文件
2006/10/09 PHP
理解和运用PHP中的多态性[译]
2011/08/02 PHP
用PHP解决的一个栈的面试题
2014/07/02 PHP
php中字符串和正则表达式详解
2014/10/23 PHP
使用PHP实现下载CSS文件中的图片
2015/12/06 PHP
php 中的closure用法详解
2017/06/12 PHP
ExtJS TabPanel beforeremove beforeclose使用说明
2010/03/31 Javascript
读jQuery之一(对象的组成)
2011/06/11 Javascript
防止按钮在短时间内被多次点击的方法
2014/03/10 Javascript
js实现局部页面打印预览原理及示例代码
2014/07/03 Javascript
纯JavaScript代码实现移动设备绘图解锁
2015/10/16 Javascript
jQuery解决$符号命名冲突
2016/06/18 Javascript
Nodejs之http的表单提交
2017/07/07 NodeJs
微信web端后退强制刷新功能的实现代码
2018/03/04 Javascript
浅谈webpack 自动刷新与解析
2018/04/09 Javascript
Layui数据表格之获取表格中所有的数据方法
2018/08/20 Javascript
jQuery实现的自定义轮播图功能详解
2018/12/28 jQuery
JSON的parse()方法介绍
2019/01/31 Javascript
使用异步controller与jQuery实现卷帘式分页
2019/06/18 jQuery
Vue实现简单的拖拽效果
2020/08/25 Javascript
Python中使用摄像头实现简单的延时摄影技术
2015/03/27 Python
Python的socket模块源码中的一些实现要点分析
2016/06/06 Python
Python算法输出1-9数组形成的结果为100的所有运算式
2017/11/03 Python
TensorFlow实现自定义Op方式
2020/02/04 Python
python-docx文件定位读取过程(尝试替换)
2020/02/13 Python
在PyCharm中安装PaddlePaddle的方法
2021/02/05 Python
利用CSS3的border-radius绘制太极及爱心图案示例
2016/05/17 HTML / CSS
Python里面如何拷贝一个对象
2014/02/17 面试题
小学教师的个人自我鉴定
2013/10/24 职场文书
自我评价个人范文
2013/12/16 职场文书
生物制药专业自我鉴定
2014/02/19 职场文书
企业宣传标语
2014/06/09 职场文书
2014年初级职称工作总结
2014/12/08 职场文书
教师聘用意向书
2015/05/11 职场文书
矛盾论读书笔记
2015/06/29 职场文书
webpack介绍使用配置教程详解webpack介绍和使用
2022/06/25 Javascript