preload 為True的情況下,會將輔助線程或者進程開在master里,加重master的負擔(master最好只是用來負責監聽worker進程)
django應用的gunicorn示例:只在主線程里開啟后台線程,worker里不啟動后台線程
gunicorn -w 5 --preload -b 127.0.0.1:8088 application_name.wsgi:application
wsgi.py文件:
""" WSGI config for erebus project. It exposes the WSGI callable as a module-level variable named ``application``. For more information on this file, see https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ """ import os from django.core.wsgi import get_wsgi_application from erebus.get_wsgi_application import get_wsgi_application from whitenoise import WhiteNoise os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'erebus.settings') application = get_wsgi_application() application = WhiteNoise(application, root='./static/') # application.add_files('/path/to/more/static/files', prefix='more-files/')
重寫的get_wsgi_application.py文件:
#!/usr/bin/env python # -*- coding: utf-8 -*- """ @Author : xxxxx @Date : 2019-02-27 19:26 @Description : 本文件的作用描述 @File : get_wsgi_application.py """ import django from django.core.handlers.wsgi import WSGIHandler def get_wsgi_application(): """ The public interface to Django's WSGI support. Return a WSGI callable. Avoids making django.core.handlers.WSGIHandler a public API, in case the internal WSGI implementation changes or moves in the future. """ django.setup(set_prefix=False) # 把主進程中的kafka consumer線程放入啟動過程中(即放入gunicorn的master進程中), # 以使用gunicorn的preload參數控制這些線程的啟動個數。 from utils.kafka_consumer_daemon import start_kafka_consumer_daemon start_kafka_consumer_daemon() return WSGIHandler()
后台線程:
# 使用線程池子 def start_kafka_consumer_daemon(): try: for _ in range(CONSUMER_THREAD_NUM): consumer = threading.Thread(target=consume_kafka) consumer.setDaemon(True) consumer.start() except Exception as e: logger.error(e) logger.error(traceback.format_exc())
配置參考:https://github.com/benoitc/gunicorn/blob/29f0394cdd381df176a3df3c25bb3fdd2486a173/examples/example_config.py
配置解讀:http://docs.gunicorn.org/en/stable/settings.html
Gunicorn+Flask中重復啟動后台線程問題
假設程序如下:
1 if __name__ == '__main__': 2 t = Thread(target=test) 3 t.start() 4 app.run(host='0.0.0.0',port=8080,debug=False)
gunicorn在啟動過程只會從flask的app文件中取出app使用,並不會執行main函數,如果希望在gunicorn中仍舊能啟動后台線程並保證后台線程不因為gunicorn的子進程重復執行,有三種方式。
1. 使用gunicorn的preload參數。在worker進程被復制(派生)之前載入應用的代碼。這種方式,線程執行代碼需要寫在app文件的全局部分,利用預加載只執行一下。
2. 使用flask的app的鈎子函數before_first_request。在before_first_request中執行線程。但這種方式必須要有第一個請求才能觸發線程啟動。
3. 使用文件鎖。這種方式與第一種方式相同,需要把線程執行的代碼寫在app文件的全局部分。在第一個子進程啟動時創建文件並加鎖,在后續的子進程啟動時判斷鎖的狀態,如果有鎖則不執行。
以上通過奇怪的操作啟動后台線程。但不推薦。可以考慮使用celery或者cron等方式實現需求。
參考:
1、https://www.jianshu.com/p/509985f98416
2、https://www.cnblogs.com/chenxianpao/p/9931483.html
3、
4、