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、

posted @ 2019-03-04 20:27  脚本小娃子  阅读(3572)  评论(0编辑  收藏  举报