为每个芹菜工人创建单独的数据库连接

时间:2016-03-17 12:26:08

标签: python mysql sqlalchemy celery django-celery

当工作人员在创建之后执行任务时,我一直在遇到奇怪的mysql问题。

我们使用django 1.3,芹菜3.1.17,djorm-ext-pool 0.5

我们以并发3开始芹菜过程。 到目前为止,我的观察是,当工作进程启动时,它们都会获得相同的mysql连接。我们记录db connection id如下所示。

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == android.R.id.home) // Press Back Icon
    {
        finish();
    }

    return super.onOptionsItemSelected(item);
}

当所有工作人员获得任务时,第一个工作成功执行,但另外两个工作人员发出奇怪的Mysql错误。它或者是“Mysql服务器消失”的错误,或者是Django抛出“DoesNotExist”错误的情况。显然,Django查询的对象确实存在。

发生此错误后,每个工作人员都会开始获取自己的数据库连接,之后我们就不会发现任何问题。

芹菜的默认行为是什么?它是否旨在共享相同的数据库连接。如果是这样,如何处理进程间通信? 理想情况下,我希望每个工作者都有不同的数据库连接。

我尝试了下面链接中提到的代码,但是没有用。 Celery Worker Database Connection Pooling

我们还修复了下面建议的芹菜代码。 https://github.com/celery/celery/issues/2453

对于那些提出问题的人,请让我知道downvote的原因。

1 个答案:

答案 0 :(得分:2)

Celery以下面的命令启动

celery -A myproject worker --loglevel=debug --concurrency=3 -Q testqueue

myproject.py作为主进程的一部分,在分配工作进程之前对mysql数据库进行了一些查询。

作为主进程中查询流的一部分,django ORM会创建一个sqlalchemy连接池(如果它尚不存在)。然后创建工作进程。

芹菜作为django fixups的一部分关闭了现有的连接。

    def close_database(self, **kwargs):
    if self._close_old_connections:
        return self._close_old_connections()  # Django 1.6
    if not self.db_reuse_max:
        return self._close_database()
    if self._db_recycles >= self.db_reuse_max * 2:
        self._db_recycles = 0
        self._close_database()
    self._db_recycles += 1

实际上可能发生的情况是,具有一个未使用的数据库连接的sqlalchemy池对象在分叉时被复制到3个工作进程。因此,3个不同的池有3个连接对象指向相同的连接文件描述符。

当被要求进行数据库连接时,工作人员在执行任务时,所有工作人员都从sqlalchemy池获取相同的未使用连接,因为当前未使用该连接。所有连接指向同一文件描述符的事实导致MySQL连接消失了。

之后创建的新连接都是新的,并且没有指向相同的套接字文件描述符。

解决方案:

在主要流程中添加

from django.db import connection
connection.cursor()

在任何导入完成之前。即在添加偶数djorm-ext-pool模块之前。

这样,所有数据库查询都将使用池外的django创建的连接。当芹菜django fixup关闭连接时,连接实际上是关闭的,而不是回到炼金术池,在分叉时应对所有工人时,炼金池没有连接。在工人要求数据库连接之后,sqlalchemy返回一个新创建的连接。