做服務器端開發的同學應該都對進程監控不會陌生,最近恰好要更換 uwsgi 為 gunicorn,而gunicorn又恰好有這么一章講進程監控,所以多研究了下。
結合之前在騰訊工作的經驗,也會講講騰訊的服務器監控是怎么做的。同時也會講下小團隊又該怎么敏捷的解決。
下面按照監控的方法依次介紹。
一、按照進程名監控
在騰訊內部所有server都是要打包發布的,而在打包過程中是需要填寫要監控的進程名,然后在crontab中定時通過ps查詢進程是否存在。
這種方法是比較簡單的方法,但是考慮到很多進程會在啟動之后改名,以及進程名存在各種特殊字符,多個進程同時存在的問題,實際操作起來并不是很舒服。
舉個簡單的例子,gunicorn啟動之后的進程名類似這樣 master: [wsgi:app],其中的方括號在grep時要記得轉義,否則就會出問題。
不過不管怎么說,這種方法在很多其他方式用不了的時候反而是最簡單的方法。
下面是用python的實現:
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代碼:
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反而相當的好用,下面是截圖:

supervisor可以很方便的管理進程,包括重啟,停止等等,而且提供了web界面和用戶驗證,可以很方便的在線管理。
但是有好處就有壞處,用了supervisor之后,就不能自己隨便的去自己重啟服務了,否則會影響supervisor的監控,這對我這種喜歡自己執行 xx.sh restart 的人實在有點太痛苦了。當然,其實要是習慣了去supervisorctl 里面start/stop/reload 之后也就還好了。
用supervisor配置gunicorn的配置項如下:
ok,目前自己常用的就是這幾種模式了,大家如果有其他選擇歡迎留言討論。
完整代碼如下:
#*/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()
新聞熱點
疑難解答
圖片精選