我们有一个Django webapp,它使用Celery 4.x异步运行任务。主要任务需要Django / Celery代码与20-100个其他服务器执行网络通信操作。我们发送给这些其他服务器的每个请求都是相同的,即用户向Django发送命令,然后告诉Celery将相同的命令发送到20-100个其他服务器中的每一个。问题是,对于Celery的基本配置,如果我们说4个工作人员,那么Celery将只与4台服务器进行通信。为解决这个问题,我们尝试将Celery与gevent一起使用。但是,gevent使用协程而不是完整的线程,对于我们的网络操作,我们使用我们自己的用C语言编写的python模块。换句话说,我们不使用python套接字或请求可以猴子修补的模块。
所以,我们想做的是以下内容。为了论证,让我们说我们的C通信模块被称为" cnet"。如果我们有20个其他必须与之通信的服务器,我们将拥有一个Celery任务函数,它可以执行以下操作:
# This uses our cnet module (written in C) to connect to a single other server
def connect_to_server(server, user_data):
response=cnet.execute_request(server,user_data)
output=do_something_with_response(response)
return output
@task
def send_something_important_to_all_servers(dest_servers, user_data):
for server in dest_servers:
t = create new thread to run connect_to_server(server, user_data)
t.start()
wait/join on all threads
return
关于如何实现这一点,我有很多问题。最初我们使用prefork池和多个工作人员,每个工人一个任务,但没有扩展。接下来我们使用了gevent,但我们没有做任何特别的事情,只是推出了4个工人的芹菜,每个工人都有大量的greenlets,就像在这里做的那样:https://groups.google.com/forum/?fromgroups=#!topic/celery-users/RNZLiNyykQQ 那当然仍然表现出同样的问题,如果我们有4个工人,那么我们只能同时运行4件事。
现在我已经读过,我们可以使用eventlet池,这有一个叫做tpool的东西,我们可以在任务中使用它来产生多个线程。文档说这对我们这样的情况特别有用,我们使用的是无法修补猴子的本机C网络模块。这对我们来说是最好的方法。即使用tpool的eventlet?有没有理由在我们的情况下使用gevent而不是eventlet,如果有的话,是否有一个相当于tpool的gevent?有没有人有一个芹菜代码的例子来处理这样的情况,即使用无法修补猴子的非原生网络代码?
答案 0 :(得分:1)
您最好的选择是在C中进行多路复用,将其作为执行所有请求的单个函数呈现给Celery。在C中也有各种各样的方法,OS线程只有一个。选择你最了解的,最舒服的。
Eventlet tpool会工作,请注意,默认情况下它的大小只有20,你可能希望增加。
如果从C模块内部使用python套接字模块,则猴子修补仍然有效。这可能是最便宜,最快捷的方式。
更新:eventlet.tpool()
收益率。它没有明确写在文档中,因为任何阻塞API都会破坏库的目的。任何数量的协程(小于或大于tpool大小)都将按预期工作。 Tpool大小仅限制同时运行的OS线程数。