DEV Community

Samuel Felipe Gacia
Samuel Felipe Gacia

Posted on

Filas no Laravel (sem Jobs)

Introdução

Recentemente, durante uma atividade no trabalho, precisei utilizar filas (SQS, especificamente) para fazer uma troca de informações assíncronas entre dois sistemas distintos (ambos utilizam Laravel).
Após alguma pesquisa na documentação do Laravel e no Google, não consegui encontrar algo que atendesse minhas expectativas, que eram:

  1. Não queria criar a mesma classe Job nos dois sistemas. E se eu não tivesse acesso ao código do outro sistema ou ele fosse escrito em outra linguagem?
  2. Queria aproveitar a implementação de filas do próprio Laravel, o que facilitaria bastante no caso de uma substituição do mecanismo de filas utilizado (SQS para banco de dados, por exemplo).

Problema

O uso do sistema de filas do Laravel, como apresentado na documentação, nos leva a pensar que só é possível utilizá-lo em um monolito, onde o sistema que publica um "job" na fila é o mesmo que vai consumí-lo.
Isso porque, ao fazer o dispatch do job, o objeto é serializado e enviado como payload para a fila, que, no momento de seu processamento, é desserializado, já com argumentos do construtor, models instanciadas e hidratadas, etc.
Para a atividade a ser realizada, eu só precisava enviar para a fila um payload em JSON.

Como resolver?

O primeiro passo foi conhecer o QueueManager do Laravel. Como você pode adivinhar, é ele quem faz o gerenciamento das filas para o framework, e é instanciado durante o boot da aplicação.
Com ele é possível obter as instâncias das filas, já configuradas, de acordo com config/queue.php.
Tendo o QueueManager, basta chamar o método connection, passando o nome da conexão desejada e um objeto de fila é retornado. Esse objeto implementa a interface Illuminate\Contracts\Queue\Queue.

use Illuminate\Contracts\Queue\Queue;
use Illuminate\Queue\QueueManager;

public function queue(): Queue
{
    $queueManager = app()->make(QueueManager::class);
    return $queueManager->connection('database');
}
Enter fullscreen mode Exit fullscreen mode

OBS 1: Aqui eu usei a conexão database para fins didáticos, mas pode ser utilizada qualquer outra suportada.
OBS 2: Em versões mais antigas do Laravel, o bind no container foi feito utilizando um alias, e você pode acessá-lo assim: app()->make('queue')

A partir disso, já seria possível chamar quaisquer métodos diretamente, mas eu fiz uma classe para ficar responsável por essa interação:

use Illuminate\Contracts\Queue\Job;
use Illuminate\Contracts\Queue\Queue;

class Dispatcher
{
    public function __construct(
        private Queue $queue
    )
    {
    }

    public function push(string $name, array $data, string $queue = null): void
    {
        $this->queue->push($name, $data, $queue);
    }

    public function pushRaw(array|string $data, string $queue = null): void
    {
        if (is_array($data)) {
            $data = json_encode($data);
        }

        $this->queue->pushRaw($data, $queue);
    }

    public function pop(string $queue = null): ?Job
    {
        return $this->queue->pop($queue);
    }
}
Enter fullscreen mode Exit fullscreen mode

Nessa classe eu recebo um objeto Queue no construtor e tenho alguns métodos para interagir com a fila.
Os métodos push e pushRaw correspondem aos métodos de mesmo nome da interface Queue, e inserem registros na fila, com a diferença de que o pushRaw não faz nenhum tratamento do payload (a conversão de array para json é por minha conta, ele espera uma string). Já o método pop retorna um registro da fila, caso exista, já mapeado para a classe Job, que possui métodos para devolvê-lo para a fila, fazer sua exclusão, etc.

Exemplos dos payloads:

  1. Com o método push:
{
  "uuid": "39a0d4d2-447e-4f38-b48b-e30ce968a9ab",
  "displayName": "some-name",
  "job": "some-name",
  "maxTries": null,
  "maxExceptions": null,
  "failOnTimeout": false,
  "backoff": null,
  "timeout": null,
  "data": {
    "name": "John Doe",
    "age": 25,
    "country": "BR"
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. Com o método pushRaw:
{
  "name": "Jane Doe",
  "age": 23,
  "country": "US"
}
Enter fullscreen mode Exit fullscreen mode

Se você quiser conferir o projeto com essa implementação, ele está disponível aqui: https://github.com/samfelgar/queue-example

Até a próxima!

Discussion (0)