【更改於2016-12-07】
強烈不建議把開celery的命令放到自己項目的main.py中通過開進程的方式去做。
因爲會報一個錯誤:missed heartbeat
找了google, stack-overflow, segmentfault 。。。 很多人提問,然而並沒有解決方案。
我把這些celery放到supervisor中,不再用程序開進程的方式去開,竟然解決了這個問題。
但,我不知道爲什麼。和同事討論了,發現可能是程序開進程可能和命令行手打不一樣。
下一篇可能會研究下python的subprocess和手動的有何不一樣。
=======================================================
背景:
最近在重構,加入RabbitMQ,在本地開發沒覺得麻煩,然而部署到測試機上,發現要敲如下命令:
celery -A task worker -l info -c 5 # 開啓rabbitmq,-l 是--loglevel的縮寫,日誌level,-c 是--concurrency 的縮寫,worker併發數
celery -A task flower # 用flower監視celery
supervisorctl> restart myapp # 重啓myapp
tail -n 50 -f myapp.log # 查看日誌
解決方案:
好尷尬,所以就在tornado的入口main.py中這麼配置:# 開輔助子進程
if SETTINGS.api_status == 1: # 正式機
cmd_list = [
'/home/www/myApp/venv/bin/python /home/www/myApp/venv/bin/celery -A task worker -c 10',
'/home/www/myApp/venv/bin/python /home/www/myApp/venv/bin/celery -A task flower',
]
elif SETTINGS.api_status == 2: # 測試機
cmd_list = [
'/home/www/myApp/myapp_env/bin/python /home/www/myApp/myapp_env/bin/celery -A task worker -c 5',
'/home/www/myApp/myapp_env/bin/python /home/www/myApp/myapp_env/bin/celery -A task flower',
]
elif SETTINGS.api_status == 3: # 開發機
cmd_list = [
'celery -A task worker -c 1',
'celery -A task flower',
]
import subprocess
p_list = []
for cmd in cmd_list:
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
logging.info(u"外部命令:%s, 進程id=%s" % (cmd, p.pid))
p_list.append(p)
# 開啓 ioloop循環
tornado.ioloop.IOLoop.instance().start()
logging.info('Exit Master')
# 殺死之前的輔助子進程
for p in p_list:
logging.info(u'殺死之前的輔助子進程,pid=%s' % p.pid)
p.kill()
意思很簡單,就是在tornado跑起來之前,先把celery跑起來。
主要是subprocess,當不指定參數env的時候,子進程是用父進程的環境,這就很好,不用去補全那環境的路徑,而且不用硬編碼。
然鵝,報錯找不到celery,python的路徑,是因爲在服務器是用supervisor+uwsgi去控制,它開的子進程沒法找到tornado父進程所在的環境。只能老老實實硬編碼去補全了python和celery路徑。
此處部署的第一個坑。
本來以爲到此爲止了,然鵝,子進程們並沒有被殺掉。
查看了下supervisor文檔,發現一個參數:killasgroup=false,當爲true時,殺死子進程們。
修改配置:vi /etc/supervisor.conf
[program:myapp]
command=/home/www/myApp/myapp_env/bin/python main.py --port=9999
directory=/home/www/myApp
startsecs=0
stopwaitsecs=0
autostart=true
autorestart=true
stdout_logfile=/home/www/myApp/log/supervisor.log
stderr_logfile=/home/www/myApp/log/supervisor.err
killasgroup=true
supervisorctl> reload 下。
此處部署的第二個坑。至此,就能單純用supervisor去控制項目開關啦,簡化了部署操作。