芹菜工人数据库连接池

时间:2013-01-25 16:38:26

标签: python postgresql connection-pooling celery

我正在使用Celery独立版(不在Django中)。我计划在多台物理机上运行一个工作任务类型。该任务执行以下操作

  1. 接受XML文档。
  2. 转换它。
  3. 使多个数据库读写。
  4. 我正在使用PostgreSQL,但这同样适用于使用连接的其他商店类型。过去,我使用数据库连接池来避免在每个请求上创建新的数据库连接,或者避免将连接打开太长时间。但是,由于每个Celery工作程序都在一个单独的进程中运行,我不确定它们实际上是如何共享池的。我错过了什么吗?我知道Celery允许你坚持从芹菜工人那里得到的结果,但这不是我想在这里做的。根据处理的数据,每个任务可以执行多个不同的更新或插入。

    从Celery工作者中访问数据库的正确方法是什么?

    是否可以跨多个工作人员/任务共享一个池,还是有其他方法可以做到这一点?

6 个答案:

答案 0 :(得分:23)

我喜欢tigeronk2关于每个工人一个连接的想法。正如他所说,Celery维护着自己的工作池,因此不需要单独的数据库连接池。 Celery Signal docs解释了在创建worker时如何进行自定义初始化,所以我将以下代码添加到我的tasks.py中,它似乎与您期望的完全一样。当工人关机时,我甚至能够关闭连接:

db_conn = None

@worker_process_init.connect
def init_worker(**kwargs):
    global db_conn
    print('Initializing database connection for worker.')
    db_conn = db.connect(DB_CONNECT_STRING)


@worker_process_shutdown.connect
def shutdown_worker(**kwargs):
    global db_conn
    if db_conn:
        print('Closing database connectionn for worker.')
        db_conn.close()

答案 1 :(得分:2)

您可以在celery config中覆盖默认行为以使每个进程具有线程化工作者而不是工作者:

CELERYD_POOL = "celery.concurrency.threads.TaskPool"

然后,您可以将共享池实例存储在任务实例上,并从每个线程任务调用中引用它。

答案 2 :(得分:2)

每个工作进程都有一个数据库连接。由于芹菜本身维护着一个工作进程池,因此您的数据库连接将始终等于芹菜工作者的数量。 翻转方面,它会将数据库连接池绑定到芹菜工作进程管理。但是,鉴于GIL在一个过程中一次只允许一个线程,这应该没问题。

答案 3 :(得分:1)

也许您可以使用pgbouncer。对于芹菜,什么都不应该改变,连接池是在进程之外完成的。我有同样的issue

('也许'因为我不确定是否会有任何副作用)

答案 4 :(得分:0)

也许,celery.concurrency.gevent可以提供池共享而不会加剧GIL。但是,它的支持仍然是“实验性的”。

psycopg2.pool.SimpleConnectionPool分享greenlets(coroutines),它们将在一个进程/线程中运行。

关于该主题的其他stack讨论的一点点。

答案 5 :(得分:0)

通过实施和监督来回馈我的发现。

欢迎反馈。

参考: 使用合并http://www.prschmid.com/2013/04/using-sqlalchemy-with-celery-tasks.html

每个工作进程(由-c k指定的prefork模式)将建立一个到DB的新连接,而不会合并或重用。 因此,如果使用池,则只能在每个工作进程级别看到池。那么游泳池大小> 1没用,但重用连接仍然可以保存连接从open&关闭。

如果每个工作进程使用一个连接,则在初始化阶段为每个工作进程建立1个DB连接(prefork模式celery -A app worker -c k)。 它保存了open& amp;反复关闭。

无论有多少工作线程(eventlet),每个工作线程(celery -A app worker -P eventlet)只建立一个与DB的连接而不进行池化或重用。 因此对于eventlet,一个芹菜进程(celery -A app worker ...)上的所有工作线程(eventlets)每次都有1个db连接。

根据芹菜文档

  

但是你需要确保你的任务不会执行阻塞调用   这将暂停工作人员的所有其他操作,直到阻止   呼叫返回。

这可能是由于MYSQL数据库连接阻塞调用的方式。