守护程序中的 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 连接超时的主要内容,如果未能解决你的问题,请参考以下文章

Java中的超时数据库连接

WebLogic 非活动连接超时和 JTA 超时

接口超时需要怎么处理

Wildfly 数据源中的空闲超时后未关闭数据库连接

web3.js 中的连接超时

grails应用程序中的Mysql连接超时,在ELB上使用mariadb galera集群