Django plus Celery/RabbitMQ 带有用于 C 套接字模块的线程

Django plus Celery/RabbitMQ with threading for C socket module

我们有一个使用 Celery 4.x 异步执行 运行 任务的 Django webapp。主要任务需要Django/Celery代码与20-100台其他服务器进行网络通信操作。我们发送到这些其他服务器的每个请求都是相同的,即用户向 Django 发送一个命令,然后 Django 告诉 Celery 向 20-100 个其他服务器中的每一个发送完全相同的命令。问题是,对于 Celery 的基本配置,如果我们说有 4 个 worker,那么 Celery 一次只能与 4 个服务器通信。为了解决这个问题,我们尝试将 Celery 与 gevent 结合使用。然而,gevent 使用协程而不是全线程,对于我们的网络操作,我们使用我们自己的 python 模块,它是用 C 编写的。换句话说,我们没有使用 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 池和多个 worker,每个 worker 一个任务,但没有扩展。接下来我们使用 gevent,但我们没有做任何特别的事情,只是启动了带有 4 个工人的 celery,每个工人都有一些大量的 greenlets,就像这里所做的一样:https://groups.google.com/forum/?fromgroups=#!topic/celery-users/RNZLiNyykQQ 那当然仍然显示出同样的问题,如果我们有 4 个工人,那么我们只能同时 运行 得到 4 个东西。

现在我读到我们可以使用 eventlet 池,它有一个叫做 tpool 的东西,我们可以在任务中使用它来生成多个线程。文档说这对于像我们这样使用无法进行猴子修补的本机 C 网络模块的情况特别有用。这对我们来说是最好的方法吗?即使用带有 tpool 的 eventlet?在我们的情况下是否有任何理由使用 gevent 而不是 eventlet,如果是这样,是否有与 tpool 等效的 gevent?有没有人有处理这种情况的芹菜代码示例,即使用不能猴子修补的非本地网络代码?

您最好的选择是在 C 中进行多路复用,将其作为执行所有请求的单个函数呈现给 Celery。在 C 中也有多种方法可以执行此操作,OS 线程只是其中一种。选择你最了解的,你最熟悉的。

Eventlet tpool 会起作用,注意,它的大小默认只有 20,您可能希望增加。

如果您在 C 模块中使用 python 套接字模块,Monkey 补丁仍然有效。这可能是最便宜和最快的方式。

更新:eventlet.tpool() 产量。它没有明确写在文档中,因为任何阻塞 API 都会破坏库的目的。任何数量的协程(小于或大于 tpool 大小)都将按预期工作。 Tpool 大小仅限制同时 OS 个线程 运行 的数量。