【问题标题】:Symfony 4 worker using doctrine not working properly : SQLSTATE[HY000] [2002] Connection timed outSymfony 4 工作人员使用学说无法正常工作:SQLSTATE [HY000] [2002] 连接超时
【发布时间】:2019-12-28 20:04:45
【问题描述】:

我正在使用带有 Symfony 4 messenger 组件的 worker。

这个工人是

  • 接收消息(来自 rabbitMQ)
  • 启动 ffmpeg
  • 对视频进行处理
  • 并将某些内容保存在数据库中。

为了在 Symfony 上配置这个工作器,我已经这样做了(中间件很重要):

// config/packages/framework.yaml
framework:
    messenger:
        buses:
            command_bus:
                middleware:
                    # each time a message is handled, the Doctrine connection
                    # is "pinged" and reconnected if it's closed. Useful
                    # if your workers run for a long time and the database
                    # connection is sometimes lost
                    - doctrine_ping_connection

                    # After handling, the Doctrine connection is closed,
                    # which can free up database connections in a worker,
                    # instead of keeping them open forever
                    - doctrine_close_connection

        transports:
            ffmpeg:
              dsn: '%env(CLOUDAMQP_URL)%'
              options:
                auto_setup: false
                exchange:
                    name: amq.topic
                    type: topic
                queues:
                  ffmpeg: ~

        routing:
            # Route your messages to the transports, for now all are AMQP messages
            'App\Api\Message\AMQPvideoFFMPEG': ffmpeg
        ## Handle multiple buses ? https://symfony.com/doc/current/messenger/multiple_buses.html
        ## When queries and command should be distinguished

然后为了了解可能导致此问题的原因,我尝试调试 Messenger 以查看中间件是否配置正确

root@b9eec429cb54:/var/www/html# php bin/console debug:messenger

Messenger
=========

command_bus
-----------

 The following messages can be dispatched:

 ------------------------------------------------------ 
  App\Api\Message\AMQPvideoFFMPEG                       
      handled by App\Api\Message\Handler\FFMPEGHandler  
 ------------------------------------------------------ 

一切看起来都还不错吧?

那怎么可能看到这个:

[2019-08-23 10:25:26] messenger.ERROR:重试 App\Api\Message\AMQPvideoFFMPEG - 重试 #1。 {"message":"[object] (App\Api\Message\AMQPvideoFFMPEG: {})","class":"App\Api\Message\AMQPvideoFFMPEG","re​​tryCount":1,"error":"[object ](Doctrine\DBAL\Exception\ConnectionException(代码:0):驱动程序中发生异常:SQLSTATE[HY000] [2002] 连接超时在 /var/www/html/vendor/doctrine/dbal/lib/Doctrine/DBAL /Driver/AbstractMySQLDriver.php:93, Doctrine\DBAL\Driver\PDOException(code: 2002): SQLSTATE[HY000] [2002] Connection timed out at /var/www/html/vendor/doctrine/dbal/lib/Doctrine/ DBAL/Driver/PDOConnection.php:31, PDOException(code: 2002): SQLSTATE[HY000] [2002] Connection timed out at /var/www/html/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection .php:27)"} []

我完全迷路了,我错过了什么吗?

这有时会发生,但它在大多数情况下都有效,我想当我的工作人员失去与数据库的连接时会发生此错误,特别是如果 ffmpeg 处理持续 7 分钟或更长时间,但这应该通过 ping 和关闭来避免连接的中间件。所以我不太清楚这里有什么问题。

【问题讨论】:

  • 你检查过 MySQL 的日志吗?你的这个中间件并不完美。如果 ffmpeg 的转换时间大于 mysql 的空闲限制,您可能会遇到连接丢失错误。相反,您应该在您的代码中“断开并重新连接”,就在任何 SQL 插入尝试之前(例如,在 Doctrine 的刷新之前)。我个人会选择一个 Doctrine 事件 preFlush/postFlush 事件订阅者:只需断开连接即可。 Doctrine 将在刷新期间重新连接。
  • emix 是个好主意,但现在我不知道如何做到这一点,只对这个处理程序有效
  • 这将是一项服务。将其注入处理程序并调用 setter setEnabled(true)。默认情况下将其设为“禁用”并且不要断开连接。
  • @emix 你能用你的解决方案写一个答案吗?我想试试,但不知道该怎么做。
  • 你能告诉我们一些关于上下文的事情吗?小型云数据库,还是成熟的数据库服务器?我问是因为我在云设置中偶然发现了非常有限的允许数据库连接(~10)。

标签: php symfony symfony-messenger


【解决方案1】:

阅读了我的中间件的代码,尤其是这个块

https://github.com/symfony/symfony/blob/4.4/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.php

class DoctrinePingConnectionMiddleware extends AbstractDoctrineMiddleware
{
    protected function handleForManager(EntityManagerInterface $entityManager, Envelope $envelope, StackInterface $stack): Envelope
    {
        $connection = $entityManager->getConnection();
        if (!$connection->ping()) {
            $connection->close();
            $connection->connect();
        }
        if (!$entityManager->isOpen()) {
            $this->managerRegistry->resetManager($this->entityManagerName);
        }
        return $stack->next()->handle($envelope, $stack);
    }
}

我们可以看到我的处理程序在连接打开后立即被调用。 我认为这种行为应该有效,但 FFMPEG 可以在很长一段时间内使用相同的 RabbitMQ 消息。所以我的处理程序的最后一步将向数据库中插入一些东西可能会提供一个 mySQL has gone away 错误,或者连接超时。

这就是为什么,我把这个 sn-p 放到一个方法中,没有调用处理程序的东西,只有与学说连接相关的代码,然后我在任何插入到我的数据库之前调用它:

public function __invoke(AMQPvideoFFMPEG $message)
    {
        // reset connection if not found
        $this->processService->testConnection();
        $process = $this->processService->find($message->getProcess());
        $this->renderServcie->updateQueue($process->getQueue(), "processing");

// some other stuff
}

testConnection() 方法在哪里

 /**
     * Reconnect if connection is aborted for some reason
     */
    public function testConnection()
    {
        $connection = $this->entityManager->getConnection();
        if (!$connection->ping()) {
            $connection->close();
            $connection->connect();
        }

    }

但在那之后我尝试了另一个问题

不支持重置非惰性管理器服务。设置 “doctrine.orm.default_entity_manager”服务懒惰并且需要 而是在您的 composer.json 文件中添加“symfony/proxy-manager-bridge”。

安装“symfony/proxy-manager-bridge”后,错误消失了。

到目前为止,还没有出现连接超时的情况。拭目以待。

【讨论】:

  • 为什么要重置管理器?制作您自己的中间件:只需 disconnect(),如果是 $envelope->getMessage() instanceof DisconnectsBeforeHandling。当然你必须创建接口,但它不需要包含任何方法。
  • 为什么要制作我自己的中间件,这个是由更有经验的开发人员完成的,不需要更换 IMO,事实上,由于处理程序是在调用之后,它不会改变任何关于 ffmpeg 和可能与 MYSQL 断开连接。
  • 他们只是人,就像你和我一样。他们也会犯错误。它们也不会涵盖所有边缘情况。您的案例是一个完美的例子:想象一个处理程序执行了很长时间(比如 5 分钟)并且您的 mysql 的超时设置设置为 1 分钟。显然,处理程序会失败。
  • @emix 是的,但现在我需要务实,让这项工作发挥作用。
  • 我在一小时前的另一条评论中告诉过你如何处理这个问题。而且你做得很好,你没有手动“连接”,Doctrine 会为你做这件事。
【解决方案2】:

在任何插入操作之前断开连接:

public function handle(…)
{
    // your time-consuming business logic

    // disconnect if needed
    if (!$this->entityManager->getConnection()->ping()) {
        $this->entityManager->getConnection()->close();
    }

    // save your work
    $this->entityManager->flush();
}

【讨论】:

  • 中间件不是真正的问题,它会在我的处理程序和 FFMPEG 开始工作之前关闭连接并再次打开它。 ffmpeg处理的漫长过程中,连接超时到达。然后你的中间件就不会再被调用了。您关于订户的解决方案对我来说更像是一个很好的解决方案,而不是这个答案。供应商中间件无论如何都可以完成这项工作。
  • 哦,好的,就像我之前说的,您必须在刷新之前断开连接 - 手动。完成转换工作后,只需将连接注入您的处理程序并手动disconnect()
猜你喜欢
  • 1970-01-01
  • 2017-09-10
  • 2019-11-29
  • 1970-01-01
  • 2017-10-19
  • 2019-06-21
  • 1970-01-01
  • 1970-01-01
  • 2015-10-21
相关资源
最近更新 更多