用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中字符串的修改及传参详解
Nov 30 Python
python+mongodb数据抓取详细介绍
Oct 25 Python
Python处理菜单消息操作示例【基于win32ui模块】
May 09 Python
浅谈tensorflow中几个随机函数的用法
Jul 27 Python
Python lxml解析HTML并用xpath获取元素的方法
Jan 02 Python
python从子线程中获得返回值的方法
Jan 30 Python
Python之时间和日期使用小结
Feb 14 Python
python使用Matplotlib改变坐标轴的默认位置
Oct 18 Python
将pymysql获取到的数据类型是tuple转化为pandas方式
May 15 Python
Python 如何查找特定类型文件
Aug 17 Python
python实现网页录音效果
Oct 26 Python
如何使用Python对NetCDF数据做空间相关分析
Apr 21 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
239军机修复记
2021/03/02 无线电
IIS6+PHP5+MySQL5+Zend Optimizer+phpMyAdmin安装配置图文教程 2009年
2009/06/08 PHP
php面向对象全攻略 (七) 继承性
2009/09/30 PHP
php 无限分类的树类代码
2009/12/03 PHP
PHP的异常处理类Exception的使用及说明
2012/06/13 PHP
PHP利用func_get_args和func_num_args函数实现函数重载实例
2014/11/12 PHP
php连接与操作PostgreSQL数据库的方法
2014/12/25 PHP
php实现对短信验证码发送次数的限制实例讲解
2021/03/04 PHP
jquery操作select option 的代码小结
2011/06/21 Javascript
javascript nextSibling 与 getNextElement(node) 使用介绍
2011/10/13 Javascript
Package.js  现代化的JavaScript项目make工具
2012/05/23 Javascript
javascript克隆对象深度介绍
2012/11/20 Javascript
javascript一元操作符(递增、递减)使用示例
2013/08/07 Javascript
js购物车实现思路及代码(个人感觉不错)
2013/12/23 Javascript
js清空form表单中的内容示例
2014/05/20 Javascript
原生JS实现美图瀑布流布局赏析
2015/09/07 Javascript
JS中的hasOwnProperty()、propertyIsEnumerable()和isPrototypeOf()
2016/08/11 Javascript
H5用户注册表单页 注册模态框!
2016/09/17 Javascript
基于jQuery实现的Ajax 验证用户名唯一性实例代码
2017/06/28 jQuery
微信小程序项目总结之点赞 删除列表 分享功能
2018/06/25 Javascript
详解如何解决vue开发请求数据跨域的问题(基于浏览器的配置解决)
2018/11/12 Javascript
[00:20]TI9观赛名额抽取Ⅱ
2019/07/24 DOTA
Python  连接字符串(join %)
2008/09/06 Python
详解Python的Twisted框架中reactor事件管理器的用法
2016/05/25 Python
Pycharm2017版本设置启动时默认自动打开项目的方法
2018/10/29 Python
浅谈numpy生成数组的零值问题
2018/11/12 Python
python selenium 获取接口数据的实现
2020/12/07 Python
Django 实现图片上传和下载功能
2020/12/31 Python
2014第二批党员干部对照“四风”找差距检查材料思想汇报
2014/09/18 职场文书
公司副总经理岗位职责
2014/10/01 职场文书
六一儿童节致辞
2015/07/31 职场文书
三八红旗手主要事迹材料
2015/11/04 职场文书
golang fmt格式“占位符”的实例用法详解
2021/07/04 Golang
SQL基础的查询语句
2021/11/11 MySQL
python绘制简单直方图(质量分布图)的方法
2022/04/21 Python
MySQL性能指标TPS+QPS+IOPS压测
2022/08/05 MySQL