用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进行数据科学工作的简单入门教程
Apr 01 Python
Python的Flask开发框架简单上手笔记
Nov 16 Python
在python3环境下的Django中使用MySQL数据库的实例
Aug 29 Python
python实现定时提取实时日志程序
Jun 22 Python
利用python打开摄像头及颜色检测方法
Aug 03 Python
对python:print打印时加u的含义详解
Dec 15 Python
python实现比较类的两个instance(对象)是否相等的方法分析
Jun 26 Python
Python数据持久化存储实现方法分析
Dec 21 Python
Python实现发票自动校核微信机器人的方法
May 22 Python
Python3通过chmod修改目录或文件权限的方法示例
Jun 08 Python
Python爬虫破解登陆哔哩哔哩的方法
Nov 17 Python
python脚本使用阿里云slb对恶意攻击进行封堵的实现
Feb 04 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利用header函数实现文件下载时直接提示保存
2009/11/12 PHP
解析在apache里面给php写虚拟目录的详细方法
2013/06/24 PHP
通过php动态传数据到highcharts
2017/04/05 PHP
JavaScript下申明对象的几种方法小结
2008/10/02 Javascript
javascript web对话框与弹出窗口
2009/02/22 Javascript
javascript 一个函数对同一元素的多个事件响应
2009/07/25 Javascript
借助script进行Http跨域请求:JSONP实现原理及代码
2013/03/19 Javascript
JS添加删除一组文本框并对输入信息加以验证判断其正确性
2013/04/11 Javascript
jQuery 仿百度输入标签插件附效果图
2014/07/04 Javascript
jquery实现的鼠标下拉滚动置顶效果
2014/07/24 Javascript
Nodejs实现短信验证码功能
2017/02/09 NodeJs
Javascript刷新页面的实例
2017/09/23 Javascript
JavaScript反射与依赖注入实例详解
2018/05/29 Javascript
对angularJs中自定义指令replace的属性详解
2018/10/09 Javascript
js的对象与函数详解
2019/01/21 Javascript
JavaScript 函数用法详解【函数定义、参数、绑定、作用域、闭包等】
2020/05/12 Javascript
js实现飞机大战小游戏
2020/08/26 Javascript
js+html+css实现手动轮播和自动轮播
2020/12/30 Javascript
[01:23]2014DOTA2国际邀请赛 球迷无处不在Ti现场世界杯受关注
2014/07/10 DOTA
[58:11]守擂赛第二周擂主赛 DeMonsTer vs Leopard
2020/04/28 DOTA
用Python实现一个简单的线程池
2015/04/07 Python
Windows上使用virtualenv搭建Python+Flask开发环境
2016/06/07 Python
python3.5 email实现发送邮件功能
2018/05/22 Python
Python 控制终端输出文字的实例
2019/07/12 Python
python opencv pytesseract 验证码识别的实现
2020/08/28 Python
Python如何实现感知器的逻辑电路
2020/12/25 Python
美国知名户外用品畅销中心:Sierra Trading Post
2016/07/19 全球购物
法国滑雪假期的专家:Ski Planet
2019/11/02 全球购物
一套PHP的笔试题
2013/05/31 面试题
传播学毕业生求职信
2013/10/11 职场文书
运动会跳远广播稿5篇
2014/09/17 职场文书
党员自我评议对照检查材料
2014/09/27 职场文书
人民的好儿女观后感
2015/06/18 职场文书
月考总结与反思
2015/10/22 职场文书
导游词之山海关
2019/12/10 职场文书
MySQL 8.0 之不可见列的基本操作
2021/05/20 MySQL