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 的想法,即使这是在子进程中完成的并且不会影响父进程。只是看起来不干净。

4

2 回答 2

4

经过一番研究,我自己弄清楚了这一点。

我上面的方法不能完全起作用,而且比需要的更复杂。需要注意的主要事情是当孩子退出时您的数据库连接会发生什么。

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

当子级退出时,父级将立即拥有无效的数据库连接,当您尝试在父级中进行查询时,您将收到类似“Mysql server has gone away”的错误。

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

因此,在我的代码中,我目前在 fork 一个孩子之后立即执行以下操作:

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

如果您在 mysql 客户端中执行“显示进程列表”,您现在将看到多个连接,每个分叉子连接一个。

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

于 2013-06-04T15:04:53.897 回答
1

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

// Close connections
$connections = $this->getContainer()
    ->get('doctrine')
    ->getConnections();
foreach($connections as $conn) {
    $this->logger->debug("Closing connection");
    $conn->close();
}
于 2013-07-18T13:14:51.483 回答