使用Doctrine为每个分叉子进程分离DB连接

时间:2013-06-03 14:26:36

标签: symfony doctrine-orm fork symfony-2.2

我有一个Symfony 2.2应用程序,它有一个命令,可以让子进程处理数据库中的实体。我很难找到强制Doctrine在每个分叉子进程中重新连接的正确方法。

我终于找到了一个解决方案(我可以在dev.log中看到每个孩子的新数据库连接),但我不确定这是否是最好的方法。我将容器传递给每个子项,然后创建一个新连接并使用该连接设置default_connection服务。但这似乎有点凌乱。还有其他想法吗?

$conn = $this->container->get('doctrine')->getConnection();
$conn2 = \Doctrine\DBAL\DriverManager::getConnection($conn->getParams(), $conn->getConfiguration(), $conn->getEventManager());
$this->container->set('doctrine.dbal.default_connection', $conn2);
$this->doctrine = $this->container->get('doctrine');
$this->doctrine->resetManager();

我不喜欢修改default_connection的想法,即使这是在子进程中完成的,也不会影响父进程。只是看起来不干净。

2 个答案:

答案 0 :(得分:4)

经过一番研究后,我自己就想出了这个。

我上面的方法并没有完全发挥作用,而且比需要的更复杂。需要注意的主要事项是当子进入时数据库连接会发生什么。

由于大多数开发人员应该知道在分叉进程时,子进程会获得所有内容的副本,包括数据库连接等资源。然而,棘手的部分是当一个分叉的孩子退出它将关闭那些资源。 这意味着孩子将关闭与父母共享的相同数据库连接。

当子进程退出时,父进程将立即建立无效的数据库连接,并且您将收到错误,例如" Mysql服务器已经消失了#34;当您尝试在父级中执行查询时。

避免此问题的一个解决方案是确保父项在分叉子项后始终重新连接到数据库。这意味着如果您分叉100个孩子,您的父母将不得不重新连接100次。如果您的孩子和父母都需要与数据库进行交互,这是一个不可避免的开销。

所以在我的代码中,我现在做了类似下面的事情,因为我分叉了一个孩子:

$conn = $this->getContainer()->get('doctrine')->getConnection();
$conn->close();
$conn->connect();

如果你做了一个" show processlist"在你的mysql客户端中,你现在可以看到多个连接,每个连接一个用于分叉的孩子。

我甚至在github上编写了一个ProcessPool库,它提供了一个易于使用的API来创建大量可以将结果返回给父级的工作进程。

答案 1 :(得分:1)

在我的shell脚本中,我在分叉之前使用了,以确保所有连接都已关闭。

// Close connections
$connections = $this->getContainer()
    ->get('doctrine')
    ->getConnections();
foreach($connections as $conn) {
    $this->logger->debug("Closing connection");
    $conn->close();
}