python 限制同一时间只执行一个
作服務器端開發的同窗應該都對進程監控不會陌生,最近剛好要更換 uwsgi 爲 gunicorn,而gunicorn又剛好有這麼一章講進程監控,因此多研究了下。python
結合以前在騰訊工做的經驗,也會講講騰訊的服務器監控是怎麼作的。同時也會講下小團隊又該怎麼敏捷的解決。web
下面按照監控的方法依次介紹。shell
1、按照進程名監控服務器
在騰訊內部全部server都是要打包發佈的,而在打包過程當中是須要填寫要監控的進程名,而後在crontab中定時經過ps查詢進程是否存在。app
這種方法是比較簡單的方法,可是考慮到不少進程會在啓動以後更名,以及進程名存在各類特殊字符,多個進程同時存在的問題,實際操做起來並非很舒服。socket
舉個簡單的例子,gunicorn啓動以後的進程名相似這樣 master: [wsgi:app],其中的方括號在grep時要記得轉義,不然就會出問題。tcp
不過無論怎麼說,這種方法在不少其餘方式用不了的時候反而是最簡單的方法。this
下面是用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)
2、按照端口監控url
這種方式以前在騰訊打包的時候也有用,可是多是進程名更直觀的緣由吧,貌似一直沒怎麼用起來。
不過如今本身在作包部署的時候,反而以爲端口監控是個最靠譜的事情了。這個也沒什麼好多說的,直接上剛寫完的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確保返回正確的包纔算正常。不過若是這麼作的話,就有點偏黑盒監控 了,好比監控寶、阿里雲監控之類的服務了。
3、經過監控server啓動進程,並以監控子進程的方式監控
這個也是在gunicorn頁面上看到的,提及來gunicorn很不厚道的把gaffer放到第一個,讓我還覺得是個很成熟的產品,結果發現連啓動都是個問題。
相反排在後面的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()