gunicorn结合django启动后台线程
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、