用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 29 Python
Python网络编程之TCP与UDP协议套接字用法示例
Feb 02 Python
Python实现对字典分别按键(key)和值(value)进行排序的方法分析
Dec 19 Python
Python之时间和日期使用小结
Feb 14 Python
Python骚操作之动态定义函数
Mar 26 Python
Python常用模块logging——日志输出功能(示例代码)
Nov 20 Python
pytorch制作自己的LMDB数据操作示例
Dec 18 Python
python实现单目标、多目标、多尺度、自定义特征的KCF跟踪算法(实例代码)
Jan 08 Python
详解Python的三种拷贝方式
Feb 11 Python
基于打开pycharm有带图片md文件卡死问题的解决
Apr 24 Python
浅谈Python里面None True False之间的区别
Jul 09 Python
python 利用zmail库发送邮件
Sep 11 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
CodeIgniter框架提示Disallowed Key Characters的解决办法
2014/04/21 PHP
php字符串函数学习之substr()
2015/03/27 PHP
php写入、删除与复制文件的方法
2015/06/20 PHP
Linux安装配置php环境的方法
2016/01/14 PHP
PHP7新功能总结
2019/04/14 PHP
javascript学习笔记(三) String 字符串类型介绍
2012/06/19 Javascript
如何让div span等元素能响应键盘事件操作指南
2012/11/13 Javascript
JavaScript验证电子邮箱的函数
2014/08/22 Javascript
jQuery实现可关闭固定于底(顶)部的工具条菜单效果
2015/11/06 Javascript
百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换
2016/02/19 Javascript
阻止表单提交按钮多次提交的完美解决方法
2016/05/16 Javascript
jQuery实现背景滑动菜单
2016/12/02 Javascript
Node.js学习之查询字符串解析querystring详解
2017/09/28 Javascript
vue组件watch属性实例讲解
2017/11/07 Javascript
js中this对象用法分析
2018/01/05 Javascript
vue+element实现表格新增、编辑、删除功能
2019/05/28 Javascript
[49:58]完美世界DOTA2联赛PWL S3 Magma vs DLG 第一场 12.18
2020/12/19 DOTA
python getopt 参数处理小示例
2009/06/09 Python
Python实现将n个点均匀地分布在球面上的方法
2015/03/12 Python
用Python制作简单的钢琴程序的教程
2015/04/01 Python
用Python创建声明性迷你语言的教程
2015/04/13 Python
运用TensorFlow进行简单实现线性回归、梯度下降示例
2018/03/05 Python
python获取文件真实链接的方法,针对于302返回码
2018/05/14 Python
Python for循环中的陷阱详解
2018/07/13 Python
Python 使用Numpy对矩阵进行转置的方法
2019/01/28 Python
Python实现九宫格式的朋友圈功能内附“马云”朋友圈
2019/05/07 Python
python实现随机加减法生成器
2020/02/24 Python
Spring Boot中使用IntelliJ IDEA插件EasyCode一键生成代码详细方法
2020/03/20 Python
Python如何把Spark数据写入ElasticSearch
2020/04/18 Python
Python常用类型转换实现代码实例
2020/07/28 Python
世界上最大的高分辨率在线图片库:Alamy
2018/07/07 全球购物
美国中西部家用医疗设备商店:Med Mart(轮椅、踏板车、升降机等)
2019/04/26 全球购物
九年级体育教学反思
2014/01/23 职场文书
益达广告词
2014/03/14 职场文书
朋友聚会开场白
2015/06/01 职场文书
联村联户简报
2015/07/21 职场文书