守护程序中的 Doctrine2 连接超时
Posted
技术标签:
【中文标题】守护程序中的 Doctrine2 连接超时【英文标题】:Doctrine2 connection timeout in daemon 【发布时间】:2012-12-27 19:56:11 【问题描述】:我有一个长时间运行的守护进程(Symfony2 命令),它从 Redis 中的工作队列中获取工作,并使用 orm 执行这些工作并写入数据库。
我注意到,当worker空闲等待工作时,由于与mysql的连接超时,worker有死亡的趋势。
具体来说,我在日志中看到:MySQL Server has gone away。
无论如何我可以让学说自动重新连接吗?或者有什么方法可以手动捕获异常并重新连接学说 orm?
谢谢
【问题讨论】:
【参考方案1】:我在我的 symfony2 beanstalkd 守护进程命令工作者中使用它:
$em = $this->getContainer()->get('doctrine')->getManager();
if ($em->getConnection()->ping() === false)
$em->getConnection()->close();
$em->getConnection()->connect();
【讨论】:
快速通知 - 在学说 2.5 及更高版本中添加了 ping【参考方案2】:看来,每当Doctrine中的EntityManager遇到任何错误/异常时,连接就会关闭,EntityManager就死了。
由于通常所有内容都包含在事务中,并且在调用 $entityManager->flush() 时执行该事务,因此您可以尝试捕获异常并尝试重新执行或放弃。
您可能希望通过更具体的类型捕获来检查异常的确切性质,无论是 PDOException 还是其他。
对于 MySQL has Gone Away 异常,您可以尝试通过重置 EntityManager 来重新连接。
$managerRegistry = $this->getContainer()->get('doctrine');
$em = $managerRegistry->getEntityManager();
$managerRegistry->resetEntityManager();
这应该使 $em 再次可用。请注意,您必须再次重新持久化所有内容,因为这个 $em 是新的。
【讨论】:
在重置经理之前是否有必要获取经理(不使用 $em 引用本身)?【参考方案3】:我在 php Gearman worker 和 Doctrine 2 上遇到了同样的问题。
我想出的最干净的解决方案是:在每个作业中关闭并重新打开连接:
<?php
public function doWork($job)
/* @var $em \Doctrine\ORM\EntityManager */
$em = Zend_Registry::getInstance()->entitymanager;
$em->getConnection()->close();
$em->getConnection()->connect();
更新
上述解决方案无法处理交易状态。这意味着 Doctrine\DBAL\Connection::close() 方法不会重置 $_transactionNestingLevel 值,因此如果您不提交事务,这将导致 Doctrine 与底层 DBMS 的翻译状态不同步.这可能会导致 Doctrine 默默地忽略开始/提交/回滚语句,并最终导致数据未提交到 DBMS。
换句话说:如果您使用此方法,请务必提交/回滚事务。
【讨论】:
我也遇到了 worker/Symfony2 的问题。只是想知道 - 如果您最终每次都关闭连接,那么在您不再需要它时(即完成工作之后)立即关闭它会不会更好? 这不会在繁忙的队列中涉及大量昂贵的重新连接吗? 是的,但它只需要几毫秒或 10 毫秒。不过,每次重建 EM 仍会触发新的连接。 如果作业本身只需要 5 毫秒,那么在任何给定时间将作业花费的时间增加 10 毫秒或更多毫秒将至少使队列长度增加一倍 - 我想我会坚持添加ping【参考方案4】:这个包装器对我有用:
https://github.com/doctrine/dbal/issues/1454
【讨论】:
我已经把它带到了当前的 Doctrine 版本,并在这里发布了它与 Zend Framework 的使用circlical.squarespace.com/blog/2013/9/12/…【参考方案5】:在您的守护进程中,您可以在每次查询之前添加重新启动连接的方法。我在使用 gaerman worker 时遇到了类似的问题:
我将连接数据保存在 zend 注册表中,所以它看起来像这样:
private function resetDoctrineConnection()
$doctrineManager = Doctrine_Manager::getInstance();
$doctrineManager->reset();
$dsn = Zend_Registry::get('dsn');
$manager = Doctrine_Manager::getInstance();
$manager->setAttribute(Doctrine_Core::ATTR_AUTO_ACCESSOR_OVERRIDE, true);
Doctrine_Manager::connection($dsn, 'doctrine');
如果它是 damenon,你可能需要静态调用它。
【讨论】:
以上是关于守护程序中的 Doctrine2 连接超时的主要内容,如果未能解决你的问题,请参考以下文章