用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编写屏幕截图程序方法
Feb 18 Python
在Python的Django框架中显示对象子集的方法
Jul 21 Python
Django中login_required装饰器的深入介绍
Nov 24 Python
Python开发的十个小贴士和技巧及长常犯错误
Sep 27 Python
python使用knn实现特征向量分类
Dec 26 Python
使用python将请求的requests headers参数格式化方法
Jan 02 Python
十个Python练手的实战项目,学会这些Python就基本没问题了(推荐)
Apr 26 Python
pycharm中显示CSS提示的知识点总结
Jul 29 Python
PyTorch的自适应池化Adaptive Pooling实例
Jan 03 Python
Python字符串函数strip()原理及用法详解
Jul 23 Python
详解tensorflow之过拟合问题实战
Nov 01 Python
Python实例教程之检索输出月份日历表
Dec 16 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学习笔记(毕业设计)
2012/02/21 PHP
PHP中数组的三种排序方法分享
2012/05/07 PHP
域名和cookie问题(域名后缀)
2012/10/10 PHP
php+highchats生成动态统计图
2014/05/21 PHP
PHP会话控制:Session与Cookie详解
2014/09/27 PHP
PHP房贷计算器实例代码,等额本息,等额本金
2017/04/01 PHP
Laravel5.4框架中视图共享数据的方法详解
2019/09/05 PHP
可以把编码转换成 gb2312编码lib.UTF8toGB2312.js
2007/08/21 Javascript
JS 表单验证大全
2011/11/23 Javascript
js中document.getElementByid、document.all和document.layers区分介绍
2011/12/08 Javascript
jQuery aminate方法定位到页面具体位置
2013/12/26 Javascript
SuperSlide2实现图片滚动特效
2014/06/20 Javascript
jQuery ajax调用WCF服务实例
2014/07/16 Javascript
switch语句的妙用(必看篇)
2016/10/03 Javascript
HTML5+Canvas调用手机拍照功能实现图片上传(下)
2017/04/21 Javascript
jQuery实现的简单拖拽功能示例【测试可用】
2018/08/14 jQuery
angularJS自定义directive之带参方法传递详解
2018/10/09 Javascript
VUE实现密码验证与提示功能
2019/10/18 Javascript
pycharm 使用心得(二)设置字体大小
2014/06/05 Python
Python实现随机选择元素功能
2017/09/14 Python
Python实现多进程共享数据的方法分析
2017/12/04 Python
详解Python下ftp上传文件linux服务器
2018/06/21 Python
python datetime中strptime用法详解
2019/08/29 Python
Python流程控制 while循环实现解析
2019/09/02 Python
Python函数默认参数常见问题及解决方案
2020/03/26 Python
解决Python在导入文件时的FileNotFoundError问题
2020/04/10 Python
灵泰克Java笔试题
2016/01/09 面试题
生物制药专业自我鉴定
2014/02/19 职场文书
经理助理岗位职责
2014/03/05 职场文书
土木工程求职信
2014/05/29 职场文书
公司行政专员岗位职责
2014/08/24 职场文书
2014银行授权委托书样本
2014/10/04 职场文书
会计电算化实训报告
2014/11/04 职场文书
新入职员工工作总结
2015/10/15 职场文书
windows安装python超详细图文教程
2021/05/21 Python
MySQL数据库⾼可⽤HA实现小结
2022/01/22 MySQL