Работник Symfony 4, использующий доктрину, не работает должным образом: SQLSTATE[HY000] [2002] Истекло время соединения

Я использую рабочий с мессенджером Symfony 4.

Этот работник

  • получение сообщения (от 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

Затем, чтобы понять, что может вызвать эту проблему, я пытаюсь отладить мессенджер, чтобы проверить, правильно ли настроено промежуточное ПО

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: повторная попытка приложения \Api\Message\AMQPvideoFFMPEG - повторить попытку #1. {"message":"[объект] (App\Api\Message\AMQPvideoFFMPEG: {})","class":"App\Api\Message\AMQPvideoFFMPEG","retryCount":1,"error":"[объект ] (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(код: 2002): SQLSTATE[HY000] [2002] Истекло время соединения для /var/www/html/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:31, PDOException(код: 2002): SQLSTATE[HY000] [2002] Истекло время соединения для /var/www/html/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:27)"} []

Я полностью потерян, я что-то пропустил?

Это иногда случается, но это работает большую часть времени, я полагаю, эта ошибка возникает, когда мой работник потерял соединение с БД, особенно если обработка ffmpeg длилась 7 минут или более, но этого следует избегать с помощью ping и промежуточного программного обеспечения близкого соединения. Так что я не совсем понимаю, в чем здесь проблема.

2 ответа

После прочтения кода моего промежуточного программного обеспечения и особенно этого блока

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 или превышению времени ожидания соединения.

Вот почему я взял этот фрагмент и поместил его в метод без вызова обработчика, заполняя только код, связанный с doctrine connect, а затем вызываю его перед любой вставкой в ​​мою БД, например:

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" как ленивую и вместо этого укажите "symfony/proxy-manager-bridge" в файле composer.json.

После установки "symfony/proxy-manager-bridge" ошибка исчезла.

До сих пор не было истекло время ожидания соединения. Ждать и смотреть.

Просто отключите перед любой операцией вставки:

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();
}
Другие вопросы по тегам