使用Python的Supervisor进行进程监控以及自动启动


Posted in Python onMay 29, 2014

做服务器端开发的同学应该都对进程监控不会陌生,最近恰好要更换 uwsgi 为 gunicorn,而gunicorn又恰好有这么一章讲进程监控,所以多研究了下。

结合之前在腾讯工作的经验,也会讲讲腾讯的服务器监控是怎么做的。同时也会讲下小团队又该怎么敏捷的解决。

下面按照监控的方法依次介绍。

一、按照进程名监控

在腾讯内部所有server都是要打包发布的,而在打包过程中是需要填写要监控的进程名,然后在crontab中定时通过ps查询进程是否存在。

这种方法是比较简单的方法,但是考虑到很多进程会在启动之后改名,以及进程名存在各种特殊字符,多个进程同时存在的问题,实际操作起来并不是很舒服。

举个简单的例子,gunicorn启动之后的进程名类似这样 master: [wsgi:app],其中的方括号在grep时要记得转义,否则就会出问题。

不过不管怎么说,这种方法在很多其他方式用不了的时候反而是最简单的方法。

下面是用python的实现:

def monitor_process(key_word, cmd):
    p1 = subprocess.Popen(['ps', '-ef'], stdout=subprocess.PIPE)
    p2 = subprocess.Popen(['grep', key_word], stdin=p1.stdout, stdout=subprocess.PIPE)
    p3 = subprocess.Popen(['grep', '-v', 'grep'], stdin=p2.stdout, stdout=subprocess.PIPE)
    lines = p3.stdout.readlines()
    if len(lines) > 0:
        return
    sys.stderr.write('process[%s] is lost, run [%s]\n' % (key_word, cmd))
    subprocess.call(cmd, shell=True)

二、按照端口监控

这种方式之前在腾讯打包的时候也有用,但是可能是进程名更直观的原因吧,貌似一直没怎么用起来。

不过现在自己在做包部署的时候,反而觉得端口监控是个最靠谱的事情了。这个也没什么好多说的,直接上刚写完的python代码:

def monitor_port(protocol, port, cmd):
    address = ('127.0.0.1', port)
    socket_type = socket.SOCK_STREAM if protocol == 'tcp' else socket.SOCK_DGRAM
    client = socket.socket(socket.AF_INET, socket_type)
    try:
        client.bind(address)
    except Exception, e:
        pass
    else:
        sys.stderr.write('port[%s-%s] is lost, run [%s]\n' % (protocol, port, cmd))
        subprocess.call(cmd, shell=True)
    finally:
        client.close()

有的朋友可能说对于tcp端口检查,其实以client的方式来connect()看是否成功会不会更好?其实我觉得这种方式也挺好的,并且对于不同的协议可以再深入处理一下,比如对http协议可以用urllib2.urlopen确保返回正确的包才算正常。不过如果这么做的话,就有点偏黑盒监控 了,比如监控宝、阿里云监控之类的服务了。

三、通过监控server启动进程,并以监控子进程的方式监控

这个也是在gunicorn页面上看到的,说起来gunicorn很不厚道的把gaffer放到第一个,让我还以为是个很成熟的产品,结果发现连启动都是个问题。

相反排在后面的supervisor反而相当的好用,下面是截图:

使用Python的Supervisor进行进程监控以及自动启动

supervisor可以很方便的管理进程,包括重启,停止等等,而且提供了web界面和用户验证,可以很方便的在线管理。

但是有好处就有坏处,用了supervisor之后,就不能自己随便的去自己重启服务了,否则会影响supervisor的监控,这对我这种喜欢自己执行 xx.sh restart 的人实在有点太痛苦了。当然,其实要是习惯了去supervisorctl 里面start/stop/reload 之后也就还好了。

用supervisor配置gunicorn的配置项如下:

[program:yuanzhaopin]
environment=PYTHON_EGG_CACHE=/tmp/.python-eggs/,PYTHONPATH=/data/release/yuanzhaopin
command=/usr/local/bin/gunicorn --debug --log-level debug --log-file /tmp/g.log wsgi:app
user=zny2008
autorestart=true
redirect_stderr=true

ok,目前自己常用的就是这几种模式了,大家如果有其他选择欢迎留言讨论。

完整代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#*/1 * * * * python /xxx/monitor.py >> /xxx/logs/monitor.log 2>&1  &
import sys
import subprocess
import os.path as op
import socket
def this_abs_path(script_name):
    return op.abspath(op.join(op.dirname(__file__), script_name))

def monitor_process(key_word, cmd):
    p1 = subprocess.Popen(['ps', '-ef'], stdout=subprocess.PIPE)
    p2 = subprocess.Popen(['grep', key_word], stdin=p1.stdout, stdout=subprocess.PIPE)
    p3 = subprocess.Popen(['grep', '-v', 'grep'], stdin=p2.stdout, stdout=subprocess.PIPE)
    lines = p3.stdout.readlines()
    if len(lines) > 0:
        return
    sys.stderr.write('process[%s] is lost, run [%s]\n' % (key_word, cmd))
    subprocess.call(cmd, shell=True)

def monitor_port(protocol, port, cmd):
    address = ('127.0.0.1', port)
    socket_type = socket.SOCK_STREAM if protocol == 'tcp' else socket.SOCK_DGRAM
    client = socket.socket(socket.AF_INET, socket_type)
    try:
        client.bind(address)
    except Exception, e:
        pass
    else:
        sys.stderr.write('port[%s-%s] is lost, run [%s]\n' % (protocol, port, cmd))
        subprocess.call(cmd, shell=True)
    finally:
        client.close()

#=============================================================================
def yuanzhaopin():
    cmd = '%s start' % this_abs_path('gun.sh')
    #monitor_process('\[yuanzhaopin\]', cmd)
    monitor_port('tcp', 8635, cmd)

def main():
    yuanzhaopin()

if __name__ == '__main__':
    main()
Python 相关文章推荐
python装饰器使用方法实例
Nov 21 Python
Python列表生成器的循环技巧分享
Mar 06 Python
Python验证文件是否可读写代码分享
Dec 11 Python
深入浅析python with语句简介
Apr 11 Python
Python使用turtle库绘制小猪佩奇(实例代码)
Jan 16 Python
django执行原始查询sql,并返回Dict字典例子
Apr 01 Python
python3 配置logging日志类的操作
Apr 08 Python
python查看矩阵的行列号以及维数方式
May 22 Python
Python SMTP发送电子邮件的示例
Sep 23 Python
学点简单的Django之第一个Django程序的实现
Feb 24 Python
python中如何对多变量连续赋值
Jun 03 Python
Python代码风格与编程习惯重要吗?
Jun 03 Python
python应用程序在windows下不出现cmd窗口的办法
May 29 #Python
python正则表达式re模块详细介绍
May 29 #Python
在python中的socket模块使用代理实例
May 29 #Python
python中stdout输出不缓存的设置方法
May 29 #Python
python两种遍历字典(dict)的方法比较
May 29 #Python
python中常用的各种数据库操作模块和连接实例
May 29 #Python
从零学Python之入门(五)缩进和选择
May 27 #Python
You might like
消息持续发送的完整例子
2006/10/09 PHP
探讨:如何使用PHP实现计算两个日期间隔的年、月、周、日数
2013/06/13 PHP
php5.2 Json不能正确处理中文、GB编码的解决方法
2014/03/28 PHP
php文件服务实现虚拟挂载其他目录示例
2014/04/17 PHP
vmware linux系统安装最新的php7图解
2019/04/14 PHP
js过滤HTML标签以及空格的思路及代码
2013/05/24 Javascript
Nodejs学习笔记之测试驱动
2015/04/16 NodeJs
JavaScript函数的调用以及参数传递
2015/10/21 Javascript
JavaScript中获取Radio被选中的值
2015/11/11 Javascript
基于jQuery实现拖拽图标到回收站并删除功能
2015/11/25 Javascript
微信小程序 出现47001 data format error原因解决办法
2017/03/10 Javascript
vue scroller返回页面记住滚动位置的实例代码
2018/01/29 Javascript
angular 组件通信的几种实现方式
2018/07/13 Javascript
微信小程序MUI导航栏透明渐变功能示例(通过改变opacity实现)
2019/01/24 Javascript
layui 实现二级弹窗弹出之后 关闭一级弹窗的方法
2019/09/18 Javascript
基于vue与element实现创建试卷相关功能(实例代码)
2020/12/07 Vue.js
[05:05]DOTA2亚洲邀请赛 战队出场仪式
2015/02/07 DOTA
Python数据分析之双色球统计单个红和蓝球哪个比例高的方法
2018/02/03 Python
python 实现数组list 添加、修改、删除的方法
2018/04/04 Python
python使用原始套接字发送二层包(链路层帧)的方法
2019/07/22 Python
Django框架中序列化和反序列化的例子
2019/08/06 Python
Python 共享变量加锁、释放详解
2019/08/28 Python
python文件读取失败怎么处理
2020/06/23 Python
世界上最大的网络主机公司:1&1
2016/10/12 全球购物
维珍澳洲航空官网:Virgin Australia
2017/09/08 全球购物
程序运行正确, 但退出时却"core dump"了,怎么回事
2014/02/19 面试题
this关键字的含义
2015/04/08 面试题
高级护理专业毕业生推荐信
2013/12/25 职场文书
中专生职业生涯规划书范文
2013/12/29 职场文书
社区关爱留守儿童活动方案
2014/08/22 职场文书
党的群众路线教育实践活动个人对照检查材料范文
2014/09/25 职场文书
工作失误检讨书(经典集锦版)
2014/10/17 职场文书
工作检讨书大全
2015/01/26 职场文书
商场圣诞节活动总结
2015/05/06 职场文书
故意杀人罪辩护词
2015/05/21 职场文书
ES6 解构赋值的原理及运用
2021/05/25 Javascript