Filas de Tarefas para Go (Golang): Visão Geral e Comparação de Ferramentas
No desenvolvimento web moderno e na criação de serviços de alta carga em Go (Golang), surge quase sempre a necessidade de executar tarefas de forma assíncrona, fora do fluxo principal de processamento de requisições HTTP. Envio de notificações por e-mail, geração de relatórios em PDF, processamento de imagens, interação com APIs externas — tudo isso exige ser movido para processos em segundo plano. É aqui que entram em cena as filas de tarefas (task queues).
Uma fila de tarefas é um mecanismo que permite adiar a execução de uma operação, garantir seu reprocessamento em caso de falha e equilibrar a carga entre vários workers. No ecossistema Go, existem várias bibliotecas maduras e em desenvolvimento ativo para resolver esse problema. Neste artigo, vamos detalhar três das abordagens mais populares: Asynq, Machinery e o trabalho direto com Redis Streams.
1. Asynq: Simplicidade e Potência baseadas em Redis
Asynq, do desenvolvedor hibiken, é provavelmente a biblioteca de filas de tarefas mais popular em Go atualmente. Ela é construída sobre o Redis e oferece uma funcionalidade rica "pronta para uso".
Principais características do Asynq:
- Monitoramento: Interface web integrada (Asynqmon) para acompanhar filas, execução de tarefas e erros.
- Agendamento: Possibilidade de adiar a execução de uma tarefa para um horário específico (sintaxe semelhante ao cron).
- Prioridades: Suporte a múltiplas filas com diferentes prioridades.
- Retry e Dead Letters: Tentativas automáticas de repetição e uma fila de "cartas mortas" para tarefas impossíveis de executar.
- Middleware: Suporte a middleware para logging, métricas, etc.
Exemplo de uso do Asynq:
package main
import ( "context" "log" "time" "github.com/hibiken/asynq")
// Define o tipo de tarefaconst TypeEmailDelivery = "email:deliver"
// Cliente para enviar tarefasfunc main() { client := asynq.NewClient(asynq.RedisClientOpt{Addr: "localhost:6379"}) defer client.Close()
// Cria a tarefa task, err := asynq.NewTask(TypeEmailDelivery, []byte(`{"user_id": 42, "template": "welcome"}`)) if err != nil { log.Fatal(err) }
// Envia a tarefa com um atraso de 5 minutos info, err := client.Enqueue(task, asynq.ProcessIn(5*time.Minute)) if err != nil { log.Fatal(err) } log.Printf("Tarefa [%s] enfileirada", info.ID)}
// Worker para processar tarefasfunc handler(ctx context.Context, t *asynq.Task) error { log.Printf("Processando tarefa: %s", t.Type()) // Lógica de envio de e-mail aqui return nil}
func startWorker() { srv := asynq.NewServer( asynq.RedisClientOpt{Addr: "localhost:6379"}, asynq.Config{Concurrency: 10}, )
mux := asynq.NewServeMux() mux.HandleFunc(TypeEmailDelivery, handler)
if err := srv.Run(mux); err != nil { log.Fatal(err) }}O Asynq é ideal para a maioria dos projetos onde o Redis já é utilizado. É simples de configurar e oferece uma excelente experiência de uso para o desenvolvedor.
2. Machinery: Tarefas Distribuídas com Brokers
Machinery é uma biblioteca mais antiga, mas muito poderosa, inspirada na biblioteca Python Celery. Ao contrário do Asynq, o Machinery não está rigidamente vinculado ao Redis e suporta vários brokers de mensagens: Redis, RabbitMQ, AWS SQS e Memória.
Principais características do Machinery:
- Flexibilidade de brokers: Fácil alternância entre diferentes brokers de mensagens.
- Resultados de tarefas: Suporte integrado para salvar e obter resultados da execução de tarefas (por exemplo, no Redis).