用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实现rest请求api示例
Apr 22 Python
为Python的web框架编写前端模版的教程
Apr 30 Python
Python3实现Web网页图片下载
Jan 28 Python
Python中%是什么意思?python中百分号如何使用?
Mar 20 Python
Python爬虫信息输入及页面的切换方法
May 11 Python
python使用turtle绘制分形树
Jun 22 Python
Python下opencv图像阈值处理的使用笔记
Aug 04 Python
linux环境下安装python虚拟环境及注意事项
Jan 07 Python
Python脚本破解压缩文件口令实例教程(zipfile)
Jun 14 Python
python中导入 train_test_split提示错误的解决
Jun 19 Python
Python 如何定义匿名或内联函数
Aug 01 Python
Python 解析库json及jsonpath pickle的实现
Aug 17 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+MSSQL分页的例子
2006/10/09 PHP
PHP include任意文件或URL介绍
2014/04/29 PHP
PHP 错误处理机制
2015/07/06 PHP
TNC vs IO BO3 第二场2.13
2021/03/10 DOTA
javascript 对象比较实现代码
2009/04/27 Javascript
javascript getElementsByClassName实现代码
2010/10/11 Javascript
javascript获取隐藏元素(display:none)的高度和宽度的方法
2014/06/06 Javascript
js实现浮动在网页右侧的简洁QQ在线客服代码
2015/09/04 Javascript
解决jquery插件:TypeError:$.browser is undefined报错的方法
2015/11/21 Javascript
Node.js 实现简单小说爬虫实例
2016/11/18 Javascript
javascript表单正则应用
2017/02/04 Javascript
vue给input file绑定函数获取当前上传的对象完美实现方法
2017/12/15 Javascript
在nginx上部署vue项目(history模式)的方法
2017/12/28 Javascript
Node.js爬取豆瓣数据实例分析
2018/03/05 Javascript
jQuery实现的响应鼠标移动方向插件用法示例【附源码下载】
2018/08/28 jQuery
前端插件之Bootstrap Dual Listbox使用教程
2019/07/23 Javascript
《javascript设计模式》学习笔记四:Javascript面向对象程序设计链式调用实例分析
2020/04/07 Javascript
vue使用keep-alive实现组件切换时保存原组件数据方法
2020/10/30 Javascript
[05:46]DOTA2英雄梦之声_第18期_陈
2014/06/20 DOTA
[01:04:08]完美世界DOTA2联赛PWL S3 INK ICE vs GXR 第一场 12.16
2020/12/18 DOTA
python发送邮件的实例代码(支持html、图片、附件)
2013/03/04 Python
Python面向对象之继承代码详解
2018/01/29 Python
对python3 一组数值的归一化处理方法详解
2018/07/11 Python
Python实现对特定列表进行从小到大排序操作示例
2019/02/11 Python
Python通过zookeeper实现分布式服务代码解析
2020/07/22 Python
详解Python中openpyxl模块基本用法
2021/02/23 Python
使用PDF.JS插件在HTML中预览PDF文件的方法
2018/08/29 HTML / CSS
Smashbox英国官网:美国知名彩妆品牌
2017/11/13 全球购物
TripAdvisor德国:全球领先的旅游网站
2017/12/07 全球购物
英国运动服、设备及配件网站:DW Sports
2019/12/04 全球购物
烹调加工管理制度
2014/02/04 职场文书
2014年电教工作总结
2014/12/19 职场文书
2015年毕业生个人自荐书
2015/03/24 职场文书
2015员工年度考核评语
2015/03/25 职场文书
2015年行政部工作总结
2015/04/28 职场文书
JMeter对MySQL数据库进行压力测试的实现步骤
2022/01/22 MySQL