用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 相关文章推荐
linux 下实现python多版本安装实践
Nov 18 Python
python将图片文件转换成base64编码的方法
Mar 14 Python
Python字符串替换实例分析
May 11 Python
python实现的希尔排序算法实例
Jul 01 Python
Django实现全文检索的方法(支持中文)
May 14 Python
python障碍式期权定价公式
Jul 19 Python
python对csv文件追加写入列的方法
Aug 01 Python
简单易懂Pytorch实战实例VGG深度网络
Aug 27 Python
Python操作redis和mongoDB的方法
Dec 19 Python
matplotlib 曲线图 和 折线图 plt.plot()实例
Apr 17 Python
python创建文本文件的简单方法
Aug 30 Python
用python对excel进行操作(读,写,修改)
Dec 25 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
比file_get_contents稳定的curl_get_contents分享
2012/01/11 PHP
获取用户Ip地址通用方法与常见安全隐患(HTTP_X_FORWARDED_FOR)
2013/06/01 PHP
PHP 年月日的三级联动实例代码
2017/05/24 PHP
Thinkphp5+plupload实现的图片上传功能示例【支持实时预览】
2019/05/08 PHP
Laravel基础-关于引入公共文件的两种方式
2019/10/18 PHP
JavaScript与DropDownList 区别分析
2010/01/01 Javascript
this和执行上下文实现代码
2010/07/01 Javascript
JQuery页面图片切换和新闻列表滚动效果的具体实现
2013/09/26 Javascript
Javascript中typeof 用法小结
2015/05/12 Javascript
javascript实现不同颜色Tab标签切换效果
2016/04/27 Javascript
js注入 黑客之路必备!
2016/09/14 Javascript
jQuery点击导航栏选中更换样式的实现代码
2017/01/23 Javascript
详解Vue 方法与事件处理器
2017/06/20 Javascript
Vue与Node.js通过socket.io通信的示例代码
2018/07/25 Javascript
JavaScript中变量提升与函数提升经典实例分析
2018/07/26 Javascript
vue watch普通监听和深度监听实例详解(数组和对象)
2018/08/16 Javascript
基于element-ui的rules中正则表达式
2018/09/04 Javascript
零基础之Node.js搭建API服务器的详解
2019/03/08 Javascript
纯js+css实现仿移动端淘宝网站的弹出详情框功能
2019/12/29 Javascript
vue实现图片上传功能
2020/05/28 Javascript
Vue向后台传数组数据,springboot接收vue传的数组数据实例
2020/11/12 Javascript
[46:00]Ti4 冒泡赛第二轮LGD vs C9 2
2014/07/14 DOTA
Python ZipFile模块详解
2013/11/01 Python
深入理解python try异常处理机制
2016/06/01 Python
老生常谈进程线程协程那些事儿
2017/07/24 Python
Python实现矩阵相乘的三种方法小结
2018/07/26 Python
详解如何为eclipse安装合适版本的python插件pydev
2018/11/04 Python
利用Python实现微信找房机器人实例教程
2019/03/10 Python
Python 基于jwt实现认证机制流程解析
2020/06/22 Python
竞选班长演讲稿
2013/12/30 职场文书
青年文明号创建承诺
2014/03/31 职场文书
机械工程学院大学生求职信
2014/05/25 职场文书
卫生院艾滋病宣传活动小结
2014/07/09 职场文书
个人总结怎么写
2015/02/26 职场文书
Python Flask请求扩展与中间件相关知识总结
2021/06/11 Python
原型和原型链 prototype和proto的区别详情
2021/11/02 Javascript