Trabalho com filas de tarefas em PHP: do simples ao complexo
Em aplicações web modernas, frequentemente surge a necessidade de executar operações que consomem muitos recursos ou são demoradas, sem fazer o usuário esperar. Envio de notificações por email, geração de relatórios, processamento de imagens, integração com APIs externas — todas essas tarefas são ideais para execução assíncrona. É aqui que as filas de tarefas (job queues) entram em cena. Neste artigo, vamos explorar detalhadamente como implementar e otimizar o trabalho com filas em PHP, começando com soluções simples até ferramentas industriais.
O que é uma fila de tarefas e por que ela é necessária?
Uma fila de tarefas (job queue) é um mecanismo que permite adiar a execução de algum trabalho para depois. Em vez de executar a tarefa imediatamente dentro de uma requisição HTTP, você a coloca em uma fila. Um worker especial ou um grupo de workers retiram as tarefas da fila e as processam em segundo plano.
Principais vantagens do uso de filas:
- Aceleração da resposta ao usuário: O usuário recebe a resposta imediatamente, enquanto o trabalho pesado é executado de forma assíncrona.
- Escalabilidade: Você pode executar vários workers para processar tarefas em paralelo.
- Confiabilidade: Se uma tarefa não for executada (por exemplo, devido a um erro temporário), ela pode ser repetida mais tarde.
- Descarregamento do servidor principal: Processos em segundo plano não bloqueiam o processamento de novas requisições HTTP.
Fila simples baseada em banco de dados
A maneira mais simples de organizar uma fila em PHP é usar uma tabela em um banco de dados (por exemplo, MySQL). Essa abordagem não requer instalação de software adicional e é perfeita para projetos pequenos.
Passo 1: Criando a tabela jobs
CREATE TABLE jobs ( id INT AUTO_INCREMENT PRIMARY KEY, queue_name VARCHAR(255) DEFAULT 'default', payload TEXT NOT NULL, status ENUM('pending', 'processing', 'completed', 'failed') DEFAULT 'pending', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, available_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, attempts INT DEFAULT 0);Passo 2: Adicionando uma tarefa à fila
function addJob(string $queueName, array $data): void { $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'password'); $stmt = $pdo->prepare( "INSERT INTO jobs (queue_name, payload, available_at) VALUES (?, ?, NOW())" ); $stmt->execute([$queueName, json_encode($data)]);}
// Exemplo de usoaddJob('emails', ['to' => 'user@example.com', 'subject' => 'Olá!']);Passo 3: Criando o worker (worker.php)
// Loop infinito para processar tarefaswhile (true) { $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'password'); // Captura da tarefa com bloqueio de linha $stmt = $pdo->prepare( "SELECT * FROM jobs WHERE status = 'pending' AND available_at <= NOW() ORDER BY id ASC LIMIT 1 FOR UPDATE" ); $stmt->execute(); $job = $stmt->fetch(PDO::FETCH_ASSOC); if ($job) { // Marca como "em processamento" $updateStmt = $pdo->prepare("UPDATE jobs SET status = 'processing' WHERE id = ?"); $updateStmt->execute([$job['id']]); try { // Execução da tarefa $data = json_decode($job['payload'], true); processJob($data); // Marca como concluída $pdo->prepare("UPDATE jobs SET status = 'completed' WHERE id = ?") ->execute([$job['id']]); } catch (Exception $e) { // Tratamento de erro $attempts = $job['attempts'] + 1; if ($attempts < 3) {