Adicionando o Laravel Horizon em nosso projeto
Tags: php, laravel, queue
Published: 2023-12-06 16:55:50

O Laravel Horizon simplifica a escalabilidade e configuração dos trabalhadores de fila, proporcionando um painel intuitivo e eficiente para o gerenciamento de filas no Laravel. Com métricas essenciais como taxa de transferência, tempo de execução e falhas, o Horizon facilita o monitoramento do sistema de filas.

Vamos fazer um exemplo prático utilizando o projeto product-api como base.

{% embed https://github.com/jmgoncalves97/product-api %}

Antes de prosseguir com o Laravel Horizon, é aconselhável possuir um bom entendimento sobre o uso de filas no Laravel.

{% embed https://medium.com/@themattos/laravel-queue-uma-poderosa-alternativa-de-fila-para-projetos-php-cd985fdde8ba %}

Assumindo que você já tenha um bom entendimento sobre Laravel Queue, vamos começar.

Instale o Horizon no projeto.

```bash
composer require laravel/horizon
```

Publique os assets.

```bash
php artisan horizon:install
```

Agora, modifique o arquivo recém-criado config/horizon.php, incluindo a fila data_sync no array queue.

```php
    'defaults' => [
        'supervisor-1' => [
            'connection' => 'redis',
            'queue' => ['default', 'data_sync'],
            'balance' => 'auto',
            'autoScalingStrategy' => 'time',
            'maxProcesses' => 1,
            'maxTime' => 0,
            'maxJobs' => 0,
            'memory' => 128,
            'tries' => 1,
            'timeout' => 60,
            'nice' => 0,
        ],
    ],
```

A configuração atual atende aos jobs despachados para a fila data_sync. No entanto, se sua aplicação envia jobs mais extensos, pesados ou enfrenta picos de alta demanda, é aconselhável ajustar as configurações para otimizar o desempenho e garantir uma execução eficiente.

Imagine um cenário em que uma fila, lida com jobs demorados que podem levar até 5 minutos para processar.

Vamos configurar um novo supervisor para esse cenário.

```php
        // example of a queue for processing long, heavy and high-demand jobs  
        'supervisor-2' => [
            'connection' => 'redis',
            'queue' => ['long_processing_jobs'],
            'balance' => 'auto',
            'autoScalingStrategy' => 'time',
            'maxProcesses' => 1,
            'maxTime' => 0,
            'maxJobs' => 0,
            'memory' => 128,
            'tries' => 3,
            'timeout' => 300,
            'nice' => 0,
        ],
```

Atenção para retry_after em config/queue.php, ele deve ser maior que o timeout definido no supervisor de config/horizon.php.

```php
        'long_processing_jobs' => [
            'driver' => 'redis',
            'connection' => 'default',
            'queue' => 'long_processing_jobs',
            'retry_after' => 450,
            'block_for' => null,
            'after_commit' => false,
        ],
```

Vamos supor que durante picos de alta demanda, nossa fila de produção enfileira mais de 100 jobs em poucos minutos.

Para otimizar o desempenho da nossa fila, podemos ajustar os parâmetros maxProcesses e balanceMaxShift. Esses ajustes devem ser feitos considerando sempre os recursos disponíveis.

```php
'environments' => [
    'production' => [
        'supervisor-2' => [
            'maxProcesses' => 20, // parallel processes
            'balanceMaxShift' => 4,
            'balanceCooldown' => 3,
            'tries' => 3,
        ],
    ],
],
```

Podemos iniciar o Horizon com o comando abaixo.

```bash
php artisan horizon
```

É recomendado o uso do Supervisor para gerenciar nosso processo do Horizon em produção, então vamos instala-lo.

```bash
sudo apt-get install supervisor
```

Agora vamos criar um arquivo de configuração em /etc/supervisor/conf.d/horizon.conf.

```ini
[program:horizon]
process_name=%(program_name)s
command=php /home/jonathan/repo/product-api/artisan horizon
autostart=true
autorestart=true
user=jonathan
redirect_stderr=true
stdout_logfile=/home/jonathan/repo/product-api/storage/logs/horizon.log
stopwaitsecs=3600
```

Altere `command` e `stdout_logfile` com o caminho da sua aplicação e user para seu usuario.

Crie o arquivo `storage/logs/horizon.log` dentro da sua aplicação.

Inicie o Supervisor.

```java
// System V
sudo service supervisor start

// Systemd
sudo systemctl start supervisor
```

Você poderá ver o log do Horizon em `storage/logs/horizon.log` e o log do Supervisor em `/var/log/supervisor/supervisord.log`.

Com as filas e supervisores do Horizon configurados e o Supervisor gerenciando o processo do Horizon, vamos rodar nossa aplicação product-api.

```bash
PHP_CLI_SERVER_WORKERS=2 php artisan serve --port 8001
```

Vamos requisitar o endpoint que despacha o job de sinc de dados.

```bash
curl http://localhost:8001/api/v1/sync/plataform1/products
```

Podemos acessar o dashboard do Horizon para acompanhar tudo com métricas detalhadas em `http://localhost:8001/horizon`.

![Horizon Dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n64o2zhmr9sgfc2hu5uh.png)

Você pode ajustar `app/Providers/HorizonServiceProvider.php` para que apenas usuários especificos vejam o dashboard em ambiente não local.

```php
protected function gate(): void
{
    Gate::define('viewHorizon', function (User $user) {
        return in_array($user->email, [
            'jonathan@example.com',
        ]);
    });
}
```

Ao tentar acessar com qualquer outro usuário, o forbidden status é retornado.

![Forbidden Status](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jhekm9yp028nu9e9tblf.png)

Ao longo deste artigo, exploramos as vantagens do Laravel Horizon para simplificar a gestão de filas no Laravel, destacando métricas essenciais. Com o exemplo prático no projeto Product-API, aprendemos a otimizar a escalabilidade e configurar trabalhadores de fila de maneira eficiente.