用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的Django框架中设置日期和字段可选的方法
Jul 17 Python
栈和队列数据结构的基本概念及其相关的Python实现
Aug 24 Python
利用 Monkey 命令操作屏幕快速滑动
Dec 07 Python
Python使用plotly绘制数据图表的方法
Jul 18 Python
python的numpy模块安装不成功简单解决方法总结
Dec 23 Python
python实现将excel文件转化成CSV格式
Mar 22 Python
pandas DataFrame索引行列的实现
Jun 04 Python
对python中url参数编码与解码的实例详解
Jul 25 Python
JetBrains PyCharm(Community版本)的下载、安装和初步使用图文教程详解
Mar 19 Python
python自动化测试三部曲之request+django实现接口测试
Oct 07 Python
Python接口自动化测试框架运行原理及流程
Nov 30 Python
Python中的xlrd模块使用整理
Jun 15 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
Zerg基本策略
2020/03/14 星际争霸
php实现的双向队列类实例
2014/09/24 PHP
thinkphp多表查询两表有重复相同字段的完美解决方法
2016/09/22 PHP
win10 apache配置虚拟主机后localhost无法使用的解决方法
2018/01/27 PHP
php web环境和命令行环境下查找php.ini的位置
2019/07/17 PHP
javascript 单例/单体模式(Singleton)
2011/04/07 Javascript
jQuery效果 slideToggle() 方法(在隐藏和显示之间切换)
2011/06/28 Javascript
Prototype源码浅析 String部分(二)
2012/01/16 Javascript
两个数组去重的JS代码
2013/12/04 Javascript
对new functionName()定义一个函数的理解
2014/05/22 Javascript
jQuery中on()方法用法实例
2015/01/19 Javascript
javascript+HTML5的Canvas实现Lab单车动画效果
2015/08/07 Javascript
AngularJS页面传参的5种方式
2017/04/01 Javascript
VUE元素的隐藏和显示(v-show指令)
2017/06/23 Javascript
zTree节点文字过多的处理方法
2017/11/24 Javascript
微信小程序wx.getImageInfo()如何获取图片信息
2018/01/26 Javascript
Vue-cli3多页面配置详解
2020/03/22 Javascript
es6数组之扩展运算符操作实例分析
2020/04/25 Javascript
Python爬虫实例_城市公交网络站点数据的爬取方法
2018/01/10 Python
Python for循环与range函数的使用详解
2019/03/23 Python
Python日志syslog使用原理详解
2020/02/18 Python
python实现简单的井字棋游戏(gui界面)
2021/01/22 Python
详解用 python-docx 创建浮动图片
2021/01/24 Python
CSS3选择器新增问题的实现
2021/01/21 HTML / CSS
在数据文件自动增长时,自动增长是否会阻塞对文件的更新
2014/05/01 面试题
新闻专业推荐信范文
2013/11/20 职场文书
最新的咖啡店创业计划书
2013/12/30 职场文书
国际会议邀请函范文
2014/01/16 职场文书
驾驶员安全责任书范本
2014/07/24 职场文书
创先争优活动党员公开承诺书
2014/08/29 职场文书
高校师德师风自我剖析材料
2014/09/29 职场文书
九九重阳节标语
2014/10/07 职场文书
党员查摆问题及整改措施
2014/10/10 职场文书
交通事故赔偿协议书
2014/10/16 职场文书
2016年保险公众宣传日活动总结
2016/04/05 职场文书
MySQL数据库之内置函数和自定义函数 function
2022/06/16 MySQL