Pull to refresh

Persistent-request библиотека для надежных запросов

Level of difficultyMedium
Reading time3 min
Views3.3K

Всем привет!

В этой статье расскажу, как решил написать библиотеку https://packagist.org/packages/xman12/persistent-request и что там внутри.

Как и любая библиотека, эта решает свои задачи, а именно гарантированное выполнение запроса и последующую обработку. Я находил, как минимум, одно решение, которое работает с подобной проблематикой — это https://temporal.io/, но система монструозная, а мне хотелось, чего-то более легкого и приземленного, поэтому я решил написать свое решение этой задачи.

Перед разработкой я обозначил для себя требования, которым должна соответствовать библиотека:

  • работать с laravel (на этом фреймворке построен сайт, где нужно было решить задачу с обработкой запросов)

  • легкое и гибкое использование

  • минимум зависимостей

Вот что в итоге вышло по зависимостям:

- php >=7.4

- guzzlehttp/guzzle >=6.3

- laravel/framework >=5.4

- laravel/serializable-closure "1.*"

На схеме ниже показал, как работает библиотека:

Ну что ж, приступим к установке и разбору. 

Для начала библиотеку нужно подключить через composer:

composer require xman12/persistent-request

Затем подключаем провайдера в config/app.php

'providers' => [PersistentRequest\ServiceProvider::class]

После подключения провайдера, нужно запустить команду, которая запускает очереди:

php artisan queue:work

Разберем более подробно на примере.

Для начала инициализируем сервис:

$requestService = app(\PersistentRequest\Services\RequestServiceInterface::class);

Теперь нужно создать объект запроса: 

$requestGuzzle = new \GuzzleHttp\Psr7\Request('get', 'https://google.com');

После нужно подготовить объект RequestDTO, и происходит вся магия:

$requestDTO = new \PersistentRequest\DTO\RequestDTO(
  $requestGuzzle, 
  \PersistentRequest\Events\SuccessEvent::class, 
  30,
  5,
  function (\GuzzleHttp\Psr7\Response $response) {
        if (200 !== $response->getStatusCode()) {
            throw new \Exception('error processed');
        }
});

Первым аргументом идет класс для запроса в guzzle.

Вторым идет \PersistentRequest\Events\SuccessEvent::class — это имя события, которое будет вызвано после успешного выполнения запроса, в него будет передан объект Psr\Http\Message\ResponseInterface, с которым можно работать в listener.

Третьим передается время в секундах, через которое библиотека повторит запрос, в случае неудачи.

Четвертым аргументом передается количество попыток после которого прекратятся запросы, тут нужно быть аккуратным если не передать тут значение то запрос будет выполняться бесконечно - пока не станет успешным. В случае если число попыток передается и по итогу времени количество отказов равняется лимиту, в таком случае очередь удаляется и отправляется событие DeleteRequestEvent, на него стоит подписаться если вы хотите отслеживать данное поведение.

Пятым аргументом передается анонимная функция, расширяющая работу с сервисом. Невалидные или валидные ответы обрабатываются, и, если нужно повторить запрос, то достаточно кинуть исключение. Так можно, не реализовывая listener, описать логику обработки ответа в анонимной функции.

Далее выполняется сам запрос:

$requestService->execute($requestDTO);

Внутри функции execute происходит запись данных запроса в бд, на случай, если он не будет валидным согласно описанной логике. Если ответ успешен, запись из базы данных удаляется.

Соберем все в кучу:

$requestService = app(\PersistentRequest\Services\RequestServiceInterface::class);
$requestGuzzle = new \GuzzleHttp\Psr7\Request('get', 'https://google.com');
$requestDTO = new \PersistentRequest\DTO\RequestDTO(
  $requestGuzzle, 
  \PersistentRequest\Events\SuccessEvent::class,
  30,
  5,
  function (\GuzzleHttp\Psr7\Response $response) {
  if (200 !== $response->getStatusCode()) {
      throw new \Exception('error processed');
  }
});

$requestService->execute($requestDTO);

Если запрос по какой-то причине «упал», то вызывается RetryRequestJob.php, который передается экземпляр RequestDTO с необходимыми параметрами.

Вот так просто написать логику запроса к сервису, который должен гарантированно выполниться. Без реализации повторных опросов на стороне самого сервиса. Этот алгоритм работы применим как в микросервисной архитектуре, где запрос/ответ не должны быть синхронными, так и в других системах, где ответ пользователю не отправляется в одной сессии.

Tags:
Hubs:
Total votes 4: ↑3 and ↓1+6
Comments8

Articles