Как выполнить длинную задачу в фоновом режиме? - PullRequest
1 голос
/ 06 февраля 2020

У меня есть приложение, созданное с использованием Symfony 5, и у меня есть скрипт, который загружает видео, расположенное на сервере, в канал авторизованного пользователя.

Вот в основном код моего контроллера:

    /**
     * Upload a video to YouTube.
     *
     * @Route("/upload_youtube/{id}", name="api_admin_video_upload_youtube", methods={"POST"}, requirements={"id" = "\d+"})
     */
    public function upload_youtube(int $id, Request $request, VideoRepository $repository, \Google_Client $googleClient): JsonResponse
    {
        $video = $repository->find($id);

        if (!$video) {
            return $this->json([], Response::HTTP_NOT_FOUND);
        }

        $data = json_decode(
            $request->getContent(),
            true
        );

        $googleClient->setRedirectUri($_SERVER['CLIENT_URL'] . '/admin/videos/youtube');
        $googleClient->fetchAccessTokenWithAuthCode($data['code']);

        $videoPath = $this->getParameter('videos_directory') . '/' . $video->getFilename();

        $service = new \Google_Service_YouTube($googleClient);
        $ytVideo = new \Google_Service_YouTube_Video();

        $ytVideoSnippet = new \Google_Service_YouTube_VideoSnippet();
        $ytVideoSnippet->setTitle($video->getTitle());
        $ytVideo->setSnippet($ytVideoSnippet);

        $ytVideoStatus = new \Google_Service_YouTube_VideoStatus();
        $ytVideoStatus->setPrivacyStatus('private');
        $ytVideo->setStatus($ytVideoStatus);

        $chunkSizeBytes = 1 * 1024 * 1024;

        $googleClient->setDefer(true);

        $insertRequest = $service->videos->insert(
            'snippet,status',
            $ytVideo
        );

        $media = new \Google_Http_MediaFileUpload($googleClient, $insertRequest, 'video/*', null, true, $chunkSizeBytes);
        $media->setFileSize(filesize($videoPath));

        $uploadStatus = false;
        $handle = fopen($videoPath, "rb");

        while (!$uploadStatus && !feof($handle)) {
            $chunk = fread($handle, $chunkSizeBytes);
            $uploadStatus = $media->nextChunk($chunk);
        }

        fclose($handle);
    }

Это в основном работает, но проблема в том, что видео может быть очень большим (10G +), поэтому оно занимает очень много времени, и в основном Nginx завершается до того, как закончится, и возвращает "шлюз 504". Тайм-аут "до завершения загрузки.

И вообще, я не хочу, чтобы пользователю приходилось ждать загрузки страницы во время загрузки.

Итак, я ищу способ, вместо того, чтобы просто немедленно запустить этот скрипт, выполнить этот скрипт в некотором фоновом потоке или асинхронным способом.

Контроллер возвращает 200 пользователю, я могу сказать ему, что загрузка происходит, и чтобы вернуться позже, чтобы проверить прогресс.

Как это сделать?

1 Ответ

1 голос
/ 06 февраля 2020

Есть много способов сделать это sh, но в основном вы хотите отделить триггер действия и его выполнение.

Просто:

  • Удалить всю тяжелую работу от вашего контроллера. Ваш контроллер должен максимально просто проверить, существует ли в вашем VideoRepository.
  • идентификатор видео, предоставленный клиентом. Хорошо, тогда вам нужно где-то хранить этот «рабочий заказ».

    Существует множество решений для этого, в зависимости от того, что вы уже установили, с какой технологией вы чувствуете себя более комфортно, и т.д. c.

    Для простоты предположим, что у вас есть таблица PendingUploads с videoId, status, createdAt и, возможно, userId. Таким образом, единственное, что должен сделать ваш контроллер, - это создать новую запись в этой таблице (возможно, проверяя, что задание еще не «поставлено в очередь», детали такого рода зависят от вашей реализации).

  • А затем верните 200 (или 202, что может быть более уместным в сложившихся обстоятельствах )

Тогда вам потребуется написать отдельный процесс.

Скорее всего, консольную команду, которую вы будете выполнять регулярно (использование cron будет самым простым способом)

При каждом выполнении этого процесса (который будет иметь все Google_Client logi c, и, вероятно, PendingUploadsRepository) будет проверять, какие задания ожидают загрузки, обрабатывать их последовательно и устанавливать status на то, что вы хотите сделать. Например, можно задать для status значение 0 (в ожидании), 1 (обработка) и 2 (обработка) и соответственно установить состояние на каждом шаге сценария.

Подробная информация о том, как это осуществить, зависит от вас. Этот вопрос будет слишком широким и самоуверенным. Выберите то, что вы уже поняли и позволяет двигаться быстрее. Если вы храните свои работы в Rabbit, Redis, в базе данных или в виде простого файла, не особенно важен . Если вы запускаете своего «потребителя» с cron или supervisor, либо.

Symfony имеет готовый компонент, который может позволить вам асинхронно отключать этот тип сообщений (Symfony Messenger ), и это довольно мило. Расследуйте, не является ли это вашей чашкой чая, хотя, если вы не собираетесь использовать ее для чего-либо другого в вашем приложении, я бы сначала упростил ее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...