在服务器端实现无间断部署Python应用的教程


Posted in Python onApril 16, 2015

 当你开始着手部署应用时,最简单的方式莫过于使用管理员身份重启my_app或者所有服务,使产品升级至当前版本。开始的时候一切都很好,但是最终你会发现一旦应用启动以后,在重启期间去尝试连接会得到众多HTTP 503 错误。

最后你可能发现Gunicorn和uWSGI可以在不关闭套接字的情况下重新加载你的应用,这样在你的应用启动时,网络请求仅仅是被延时了一点点。只要你的应用不会花费很长时间在启动上,它就会工作的很好。不幸的是,现有的许多应用可能会花费1分钟的时间在启动上,对于等待在套接字上的链接来说,这太长了。

Gunicorn使用kill -HUP $PID,通过关闭所有工作进程,然后再启动它们来重新加载。但是工作进程缓慢的初始化过程往往会导致问题的产生。uWSGI使用链式重载,它每次只会启动一个工作进程。我需要对Tornado的支持,它当前并不十分适合uWSGI。

使用负载均衡器

一种常见的技术是从负载均衡器中移除单个服务器,升级/重启应用,然后再把它加载回来。我们正在使用负载均衡器,但是为了调度整个过程,在配置节点的时候需要协调使用HAProxy来管理套接字。我们当前的部署方案是同时部署到所有节点,而不是一个接一个的来,一个相当大的变化。在等待LBs(译注:负载均衡器)将节点移出池期间,可以使用404'ing状态页来欺骗healthcheck。这比我想要的时间要多一点,对于每个服务器来说,两次healthcheck失败间隔5秒钟,这包括了升级完成后web进程恢复的时间。

Gunicorn 重载 ++

Gunicorn会自动重启失败的web进程,所以它可能会杀掉每个进程,在其间休眠,直到所有的子进程执行完毕。这很有效,不过如果应用启动的次数变动显著的话,我们要么会为重启等待过长时间,要么会等待不长的时间并承担一些故障宕机的风险。

因为Gunicorn包含了指向应用的Python钩子,所以完全可能写出一小段代码,在工作进程准备就绪的时候通知重启进程。Gunicorn并不包含需要的钩子,但做出改变非常简单。在新版本发布前它需要一些修改。

现在重启进程发挥了这样的事实优势,就是说单个的soket具有接受连接的多个进程。重启只会极微弱的减少服务能力(1/N),但我们因此可以继续处理流量而无需让连接等待过长时间。

这种进程一般是这样的
 

for child_pid of gunicorn-master:
 kill child_pid
 wait for app startup

我的第一个版本使用shell和nc来监听应用启动的UDP数据包。尽管将我们的进程管理器集成到shell环境比我预想的要麻烦一点,但它工作的很好。

重启脚本被调用的时候应该带上Gunicorn的PID,就是masterrestart.sh的 $PID
 

echo 'Killing children of ' $1;
 
children=$(pgrep -P $1)
for child in $children
do
 echo 'Killing' $child
 kill $child
 response=$(timeout 60 nc -w 0 -ul 4012)
 if [ "$response" != '200 OK' ]; then
  echo 'BROKEN'
  exit 1;
 fi
done

在串联上post_worker_init脚本,以便app运行的时候通知重启脚本。
 

import socket
import time
 
def post_worker_init(worker):
 _send_udp('200 OK\n')
 
def _send_udp(message):
 
 udp_ip = "127.0.0.1"
 udp_port = 4012
 
 sock = socket.socket(socket.AF_INET, # Internet
       socket.SOCK_DGRAM) # UDP
 sock.sendto(message, (udp_ip, udp_port))
如果我们有这样一个WSGI( Python Web Server Gateway Interface)应用:
 
from werkzeug.wrappers import Request, Response
 
@Request.application
def application(request):
 resp = Response('Hello World!')
 if request.path == '/_status':
  resp.status = '200 OK'
 else:
  resp.status ='404 Not Found'
 return resp

我们甚至可以去做检查/_status页面之类的事情,以此来验证应用是否已运行。
 

def post_worker_init(worker):
 env = {
  'REQUEST_METHOD': 'GET',
  'PATH_INFO': '/_status',
 }
 def start_response(*args, **kwargs):
  _send_udp(args[0])
 
 worker.wsgi(env, start_response)

注意不要试图在这个健康检测中运行太多的应用,如果不管什么原因你的post_worker_init产生了一个错误,那么工作进程将会退出,并阻止应用的启动。在你检查可能失效的DB链接的时候这会是一个问题,即使你的应用可以工作,它也无法再次启动。

现在通过一分钟的应用启动,我们实现了滚动重启,而无需停止应用或者丢弃任何链接!

Python 相关文章推荐
python调用新浪微博API项目实践
Jul 28 Python
深入理解Javascript中的this关键字
Mar 27 Python
Python实现扫描局域网活动ip(扫描在线电脑)
Apr 28 Python
Python中处理字符串的相关的len()方法的使用简介
May 19 Python
Python3处理文件中每个词的方法
May 22 Python
python互斥锁、加锁、同步机制、异步通信知识总结
Feb 11 Python
基于Django ORM、一对一、一对多、多对多的全面讲解
Jul 26 Python
Django用户认证系统 组与权限解析
Aug 02 Python
基于python的BP神经网络及异或实现过程解析
Sep 30 Python
使用SQLAlchemy操作数据库表过程解析
Jun 10 Python
Python离线安装各种库及pip的方法
Nov 28 Python
Python中文纠错的简单实现
Jul 07 Python
使用Protocol Buffers的C语言拓展提速Python程序的示例
Apr 16 #Python
使用Python编写一个模仿CPU工作的程序
Apr 16 #Python
利用Python中的mock库对Python代码进行模拟测试
Apr 16 #Python
使用Python脚本来控制Windows Azure的简单教程
Apr 16 #Python
在Python下利用OpenCV来旋转图像的教程
Apr 16 #Python
在Python中使用Neo4j数据库的教程
Apr 16 #Python
使用Python的Zato发送AMQP消息的教程
Apr 16 #Python
You might like
玩家交还《星际争霸》原始码光盘 暴雪报以厚礼
2017/05/05 星际争霸
实现分十页分向前十页向后十页的处理
2006/10/09 PHP
php学习之 循环结构实现代码
2011/06/09 PHP
PHP页面中文乱码分析
2013/10/29 PHP
PHP编写文件多服务器同步程序
2016/07/02 PHP
PHP使用 Pear 进行安装和卸载包的方法详解
2019/07/08 PHP
jquery实现类似淘宝星星评分功能有截图
2014/09/15 Javascript
javascript组合使用构造函数模式和原型模式实例
2015/06/04 Javascript
jquery选择器中的空格与大于号>、加号+与波浪号~的区别介绍
2016/06/24 Javascript
touch.js 拖动、缩放、旋转 (鼠标手势)功能代码
2017/02/04 Javascript
VueJs 搭建Axios接口请求工具
2017/11/20 Javascript
vue数组对象排序的实现代码
2018/06/20 Javascript
Vue.js下拉菜单组件使用方法详解
2019/10/19 Javascript
js实现随机div颜色位置 类似满天星效果
2019/10/24 Javascript
VUEX采坑之路之获取不到$store的解决方法
2019/11/08 Javascript
js与jquery获取input输入框中的值实例讲解
2020/02/27 jQuery
《javascript设计模式》学习笔记四:Javascript面向对象程序设计链式调用实例分析
2020/04/07 Javascript
Python性能优化的20条建议
2014/10/25 Python
Python中的进程分支fork和exec详解
2015/04/11 Python
Python使用cx_Oracle模块将oracle中数据导出到csv文件的方法
2015/05/16 Python
rabbitmq(中间消息代理)在python中的使用详解
2017/12/14 Python
python线程池threadpool使用篇
2018/04/27 Python
python XlsxWriter模块创建aexcel表格的实例讲解
2018/05/03 Python
python的pip安装以及使用教程
2018/09/18 Python
Python定时发送消息的脚本:每天跟你女朋友说晚安
2018/10/21 Python
python装饰器的特性原理详解
2019/12/25 Python
Python flask框架实现浏览器点击自定义跳转页面
2020/06/04 Python
英文自荐信
2013/12/19 职场文书
本科毕业生求职自荐信
2014/02/03 职场文书
团购业务员岗位职责
2014/03/15 职场文书
三八妇女节活动主持词
2014/03/17 职场文书
三好学生个人先进事迹材料
2014/05/17 职场文书
群众路线剖析材料怎么写
2014/10/09 职场文书
大学生上课迟到检讨书
2014/10/15 职场文书
人生一定要学会的三样东西:放下、忘记、珍惜
2019/08/21 职场文书
Python可视化学习之seaborn绘制矩阵图详解
2022/02/24 Python