Por que Go é a linguagem ideal para filas e tarefas em segundo plano?
\n\nSe você já escreveu um backend em Python ou Node.js, certamente já enfrentou o problema: a aplicação processa uma requisição e, ao mesmo tempo, precisa enviar um e-mail, gerar um relatório ou processar um arquivo carregado. Se fizer isso de forma síncrona — o usuário espera. Se fizer em uma thread separada — começam as complicações com locks e race conditions.
\n\nGo resolve esse problema de forma elegante e nativa. Goroutines e canais (channels) são mecanismos de concorrência embutidos que tornam o desenvolvimento de filas e tarefas em segundo plano em Go simples e seguro. Neste artigo, vamos abordar como organizar o processamento em segundo plano usando tanto a biblioteca padrão quanto ferramentas populares como RabbitMQ. Você verá exemplos reais de código que pode aplicar imediatamente no seu projeto.
\n\nFundamentos: goroutines e canais
\n\nAntes de mergulharmos nas filas, vamos relembrar o básico. Uma goroutine é uma linha de execução leve, iniciada com a palavra-chave go. Um canal é um "tubo" tipado através do qual as goroutines se comunicam entre si.
Exemplo simples: envio assíncrono de e-mail
\n\npackage main\n\nimport (\n "fmt"\n "time"\n)\n\nfunc sendEmail(email string) {\n time.Sleep(2 * time.Second) // simula uma operação longa\n fmt.Println("Email sent to", email)\n}\n\nfunc main() {\n fmt.Println("Start")\n go sendEmail("user@example.com") // execução em segundo plano\n fmt.Println("Continue processing...")\n time.Sleep(3 * time.Second) // aguarda a conclusão da goroutine\n}\n\nAqui, sendEmail é executada em segundo plano, e o fluxo principal não é bloqueado. Mas e se precisar processar 1000 e-mails? Iniciar 1000 goroutines não é uma boa ideia: elas começarão a competir por recursos. É aí que entram os canais e o padrão Worker Pool.
Padrão Worker Pool em Go
\n\nWorker Pool é uma abordagem clássica para organizar filas e tarefas em segundo plano em Go. Você tem um número fixo de workers (goroutines) que escutam um único canal de tarefas. As tarefas chegam do fluxo principal ou de um manipulador HTTP.
\n\nImplementação do Worker Pool
\n\npackage main\n\nimport (\n "fmt"\n "sync"\n "time"\n)\n\ntype Task struct {\n ID int\n Data string\n}\n\nfunc worker(id int, tasks <-chan Task, wg *sync.WaitGroup) {\n defer wg.Done()\n for task := range tasks {\n fmt.Printf("Worker %d processing task %d: %s\\n", id, task.ID, task.Data)\n time.Sleep(1 * time.Second) // simula trabalho\n }\n}\n\nfunc main() {\n const numWorkers = 3\n tasks := make(chan Task, 10) // canal com buffer\n\n var wg sync.WaitGroup\n\n // Inicia os workers\n for i := 1; i <= numWorkers; i++ {\n wg.Add(1)\n go worker(i, tasks, &wg)\n }\n\n // Envia as tarefas\n for i := 1; i <= 10; i++ {\n tasks <- Task{ID: i, Data: fmt.Sprintf("Task %d", i)}\n }\n close(tasks) // fecha o canal para que os workers terminem\n\n wg.Wait()\n fmt.Println("All tasks completed")\n}\n\nEste código cria 3 workers que processam 10 tarefas. O canal com buffer permite enviar tarefas sem bloqueio, até que o buffer esteja cheio. O padrão é ideal para tarefas em segundo plano em Golang em aplicações web: você recebe uma requisição, coloca a tarefa no canal e responde imediatamente ao usuário.
\n\nFilas em Go com RabbitMQ
\n\nPara sistemas distribuídos, canais em memória não são suficientes — é necessário um armazenamento externo de filas. RabbitMQ é um dos brokers de mensagens mais populares. Vamos ver como conectá-lo a uma aplicação Go.
\n\nInstalação e conexão
\n\nPrimeiro, instale a biblioteca amqp:
go get github.com/rabbitmq/amqp091-go\n\n