Chinaunix首页 | 论坛 | 博客
  • 博客访问: 30001367
  • 博文数量: 2065
  • 博客积分: 10377
  • 博客等级: 上将
  • 技术积分: 21525
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-04 17:50
文章分类

全部博文(2065)

文章存档

2012年(2)

2011年(19)

2010年(1160)

2009年(969)

2008年(153)

分类: 系统运维

2010-03-10 08:31:37

昨天有个服务器出了点小问题,PHP FastCGI 进程无缘无故就死在那里了,造成 Nginx 不能和 FastCGI 通信,不能解析 PHP 页面,只能看到 Nginx 的 默认 HTML 页面。登录到服务器检查日志也没有找到原因,重启 FastCGI 后恢复正常。,如果服务关闭的话会自动重启,但是这里 FastCGI 服务并没有关闭,只不过由于某种原因不能和 Nginx 通信,所以 monit 误认为 FastCGI 运行正常没有执行重启 FastCGI 的命令。

今天写了一个 Python 脚本用来后台监测 nginx 的日志,如果在日志里发现 111: Connection refused 或者 104: Connection reset by peer 等错误就 kill 掉所有 php-cgi 进程后重启服务。程序有几个地方需要说明:

1、如果不先 kill 直接运行 /etc/init.d/php-cgi restart 重启服务有时候不管用,因为这时候 php-cgi 只是驻留在内存里的死进程而而已不能执行任何命令,所以需要 kill 全部 php-cgi 进程后再启动;
2、读 nginx 的 log 文件的时候要小心,log 文件通常很大,如果用 python 的普通读文件函数会把整个文件读出来后再处理,速度很慢,也会占用大量内存,所以最好用 tail 截取文件,我们只需要分析最后部分(也是最近)的记录即可;
3、程序通过 /var/run/php_cgi.pid 来判断 PHP FastCGI 是否正在运行,通过 /var/log/nginx/error.log 来判断 Nginx/FastCGI 是否工作正常,每600秒检查一次,如果工作不正常 sleep 2秒后重启 php-cgi;
4、本程序可扩展到重启其他服务,比如通过 /var/run/lighttpd.pid 来判断 lighttpd 是否需要重启;
5、程序中 daemonize 函数来自 Python Cookbook(O’Reilly ) 一书。

程序使用

拷贝下面代码做一定修改以后保存为 checkphpcgi,增加文件可执行权限后用 root 运行程序:

# chmod +x checkphpcgi

# ./checkphpcgi
usage: ./checkphpcgi start|stop|restart

# ./checkphpcgi start

如果想要停止程序:

# ./checkphpcgi stop

程序代码

#!/usr/bin/python

# Monitoring PHP/FastCGI processes, restart FastCGI if they:
# 1. crashed or
# 2. simply cannot communicate with Nginx
#
# written by

import sys, os, time
from signal import SIGTERM

pidfile = '/var/run/checkphpcgi.pid'
phpcgi_pidfile = '/var/run/php_cgi.pid'
phpcgi_command = '/etc/init.d/php_cgi restart'
nginx_logfile = '/var/log/nginx/error.log'
kill_phpcgi = 'killall -9 php-cgi'

def main():
argc = len(sys.argv)
if argc != 2:
print "usage: ./checkphpcgi start|stop|restart"
sys.exit(0)
action = sys.argv[1]
if action == 'start':
start()
elif action == 'stop':
stop()
elif action == 'restart':
stop()
start()

def start():
daemonize()

while True:
try:
f = file(phpcgi_pidfile, 'r')
pid = int(f.read().strip())
f.close()
except IOError:
pid = None

if not pid:
os.system(phpcgi_command)
elif pid > 0:
log = '/usr/bin/tail ' + nginx_logfile + ' > /tmp/nginx.log'
os.system(log)
f = file('/tmp/nginx.log')
text = f.read()
if text.find("111: Connection refused") or text.find("104: Connection reset by peer"):
os.system(kill_phpcgi)
time.sleep(2)
os.system(phpcgi_command)
f.close
os.remove('/tmp/nginx.log')
time.sleep(600)

def stop():
try:
f = file(pidfile, 'r')
pid = int(f.read().strip())
f.close()
except IOError:
pid = None
return

try:
os.kill(pid, SIGTERM)
os.remove(pidfile)
except OSError, err:
sys.stderr.write("not found checkphpcgi\n")

def daemonize(stdin = '/dev/null', stdout = 'dev/null', stderr='/dev/null'):
try:
pid = os.fork()
if pid > 0:
sys.exit(0)
except OSError, e:
sys.stderr.write("fork failed\n")
sys.exit(1)

os.chdir("/")
os.umask(0)
os.setsid()

try:
pid = os.fork()
if pid > 0:
sys.exit(0)
except OSError, e:
sys.stderr.write("fork failed\n")
sys.exit(1)

sys.stdout.flush()
sys.stderr.flush()
si = file(stdin, 'r')
so = file(stdout, 'a+')
se = file(stderr, 'a+', 0)
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())

f = open(pidfile, "w")
f.write("%d" % os.getpid())
f.close()

if __name__=="__main__":
main()
阅读(1369) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-03-10 08:32:52

选择了一个领域一个行业然后努力去将其做精。关键是要自已能够坚持去做!