HENNG

用 Python 来守护你的程序

之前项目里有个程序,含有 HTTP 上传的相关逻辑;长时间跑,偶尔会出现连接超时的异常,然后再也不能进行新的上传。经过多方面的排查,我最终定位到是该程序里使用的第三方 sdk 的问题,该 sdk 中在某种特定场景下没有正确释放连接,导致一段时间后,获取不到任何可用连接,于是有了连接超时的异常。

而让人头疼的是,该 sdk 的项目组目前没人维护,基本处于“荒废”的状态。没办法,指望对方修复 bug 是不太现实了,只能自己这边想办法绕过或处理。于是,采用最简单的重启大法,即出现该异常时,重启我的程序即可恢复。之前也提到过,要开始学习下 Python/PHP 这类语言,这次便用 Python 写了个“看门狗”,一个简单的守护任务。逻辑很简单,扫描程序的 log,出现特定异常时,调用该程序的 stop/start 两个 shell 脚本来完成重启逻辑。

不多说,直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/python

import subprocess
import select
import os

# set JAVA_HOME
os.environ["JAVA_HOME"] = "/AAA/BBB/jdk1.6"
startShell = 'sh /AAA/BBB/my-program/bin/start.sh'
stopShell = 'sh /AAA/BBB/my-program/bin/stop.sh'
filename = '/AAA/BBB/logs/my-program.log'

f = subprocess.Popen(['tail', '-F', filename], \
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p = select.poll()
p.register(f.stdout)

while True:
if p.poll(1):
line = f.stdout.readline()
errMsg = 'Caused by: org.apache.http.conn.ConnectionPoolTimeoutException: ' \
'Timeout waiting for connection from pool'
if errMsg in line:
print line
try:
# before python2.7, there's no 'subprocess.check_output'
stopOutput = subprocess.check_output(['/bin/sh', '-c', stopShell])
except:
stopOutput = subprocess.Popen(['/bin/sh', '-c', stopShell], stdout=subprocess.PIPE).communicate()[0]
if 'stop finished' in stopOutput:
subprocess.Popen(['/bin/sh', '-c', startShell])

逻辑比较简单,但这里我提出几点部署过程中容易踩坑的地方。

第一点,我使用 ansible 批量执行,我的程序是一个 JAVA 程序,而不同主机上环境变量可能有所不同,所以我指定一个统一的 JDK:

1
os.environ["JAVA_HOME"] = "/AAA/BBB/jdk1.6"

第二点,我发现有的 Python 守护任务执行失败了,原因是 subprocess.check_output 这个方法在 Python 2.7 版本之前好像是不支持的,而由于历史等原因,某些主机上的 Python 版本是比较低的,还得使用 Popen:

1
subprocess.Popen(['/bin/sh', '-c', stopShell], stdout=subprocess.PIPE).communicate()[0]

最后一点,让 Python 守护任务在后台一直跑着,输出必要的日志。

1
nohup python -u python_watch_dog.py > python_watch_dog.log 2>&1 &