用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实现从字符串中找出字符1的位置以及个数的方法
Aug 25 Python
python getopt详解及简单实例
Dec 30 Python
Python多进程并发与多线程并发编程实例总结
Feb 08 Python
python脚本实现验证码识别
Jun 07 Python
深入分析python中整型不会溢出问题
Jun 18 Python
Django 实现前端图片压缩功能的方法
Aug 07 Python
Python数据持久化存储实现方法分析
Dec 21 Python
Python内置方法和属性应用:反射和单例(推荐)
Jun 19 Python
使用Python项目生成所有依赖包的清单方式
Jul 13 Python
2020版Python学习路线图(附学习资料)
Sep 15 Python
Python中的特殊方法以及应用详解
Sep 20 Python
python tkinter的消息框模块(messagebox,simpledialog)
Nov 07 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
默默简单的写了一个模板引擎
2007/01/02 PHP
PHP之短标签开启设置
2013/06/17 PHP
PHP中创建图像并绘制文字的例子
2014/11/19 PHP
thinkPHP5.0框架事务处理操作简单示例
2018/09/07 PHP
js chrome浏览器判断代码
2010/03/28 Javascript
js中字符替换函数String.replace()使用技巧
2011/08/14 Javascript
JavaScript代码生成PDF文件的方法
2016/02/26 Javascript
JavaScript实战之带收放动画效果的导航菜单
2016/08/16 Javascript
angularJS 指令封装回到顶部示例详解
2017/01/22 Javascript
js实现省市级联效果分享
2017/08/10 Javascript
基于vue的短信验证码倒计时demo
2017/09/13 Javascript
详解vuex 渐进式教程实例代码
2018/11/27 Javascript
JavaScript cookie原理及使用实例
2020/05/08 Javascript
jQuery 添加元素和删除元素的方法
2020/07/15 jQuery
vue穿梭框实现上下移动
2021/01/29 Vue.js
详解Python中的变量及其命名和打印
2016/03/11 Python
利用python发送和接收邮件
2016/09/27 Python
利用python获取当前日期前后N天或N月日期的方法示例
2017/07/30 Python
详细分析python3的reduce函数
2017/12/05 Python
python+pyqt5编写md5生成器
2019/03/18 Python
Python如何使用paramiko模块连接linux
2020/03/18 Python
英国最大的百货公司:Harrods
2016/08/18 全球购物
施华洛世奇天猫官方旗舰店:SWAROVSKI
2017/04/17 全球购物
Java面试题:为什么要用Java
2012/05/11 面试题
IBatis持久层技术
2016/07/18 面试题
创联软件面试题笔试题
2012/10/07 面试题
实习生自我鉴定范文
2013/12/05 职场文书
投资意向书范本
2014/04/01 职场文书
2014年端午节演讲稿范文
2014/05/23 职场文书
2014教师年度思想工作总结
2014/11/10 职场文书
2014年优秀班主任工作总结
2014/12/16 职场文书
教师听课评语大全
2014/12/31 职场文书
爱国主义电影观后感
2015/06/18 职场文书
英语读书笔记
2015/07/02 职场文书
假如给我三天光明:舟逆水而行,人遇挫而达 
2019/10/29 职场文书
CSS三大特性继承性、层叠性和优先级详解
2022/01/18 HTML / CSS