Gunicorn Django 多线程

3
我在寻找关于Gunicorn/Django进程/线程生命周期的文档方面遇到了问题。
假设在process_response()中间件钩子期间生成一个守护线程。据我所知,此线程不会阻塞HTTP响应。但是,它是否会阻塞从中生成的线程?在工作进程准备处理另一个请求之前,Gunicorn是否等待该线程完成并将其加入回主线程,还是将分离该线程?

data_collection/tasks.py:

from celery import shared_task

@shared_task(ignore_result=True)
def add_event(event_name, event_body):
    ...
    client.add_event(event_name, event_body)

data_collection/middleware.py:

import threading
from data_collection.tasks import add_event

class DataCollectionMiddleware:
    def process_response(self, request, response):
        ...
        thread = threading.Thread(target=add_event.delay, args=("Page_Views", event_body))
        thread.setDaemon(True)
        thread.start()

更多细节:
我编写了一个自定义的中间件类,将一些数据发送到外部队列(RabbitMQ),稍后由celery worker异步检索和处理。我不希望这个“通过网络”排队调用阻塞客户端的响应,因此我在“守护进程”线程中包装该函数(add_event.delay())(参见http://www.artfulcode.net/articles/threading-django/)。如果出现网络故障并且重试策略具有长时间限制,则此线程可能会运行很长时间。在这种情况下,这些线程是否会阻塞我的Gunicorn工作进程?
我阅读了这个问题,但我不确定我的线程是否干扰了“Worker的主循环”: Danger to having long lasting (non-deamon) threads in a Django/Gunicorn app?

BigWig队列正在您的局域网中运行,对吧?我无法想象它需要那么长时间。考虑记录“发送到队列”的时间。如果它总是很快,那就不要费心再开一个线程了。多线程/进程程序总是很难调试的 :) - johntellsall
@shavenwarthog 它实际上是一个托管在外部的队列。它需要大约750毫秒或0毫秒才能完成enqueue调用... 我假设celery库在enqueue时惰性地打开套接字,并且这些套接字在空闲时间过长后被池化和关闭。/无论如何/我对这个问题的理论和实际解决方法同样感兴趣 :) - Ben Simmons
嗯,问题是,如果你有线程A(即来自Gunicorn),并且你创建子线程B(即“发送数据”线程),如果你想让线程A返回但线程B正在忙碌会发生什么?我不知道。我通常以最简单的方式编写代码,等待所有任务完成。如果后来它不够快,可以进行优化。很多时候任务已经足够快了,或者整个项目也可能被取消 :) - johntellsall
1个回答

5
没有什么特别的地方,和从Gunicorn工作线程中派生的线程无关。
一旦你派生了一个线程,它将并行执行直到完成或死亡。Gunicorn不知道这些从工作线程中派生出来的线程,因此它不会尝试加入它们,因此工作线程不会等待子线程完成。此外,线程的后台性质没有影响;后台只是意味着该线程不会对进程的“存活”做出贡献,并且将在进程退出时运行,然后被自动终止。
如果您想在重用相同的工作线程之前等待这些线程完成,则必须在WSGI应用程序(例如django.core.handlers.wsgi.WSGIHandler.__call__())返回之前执行此操作。或编写一些疯狂的Gunicorn猴子补丁来跟踪子线程。
简而言之,你可以通过从工作线程中派生长时间运行的子线程来无限增加线程。最好保证它们在一定的时间范围内完成,以避免问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接