autopaho é o gerenciador de conexões do github.com/eclipse/paho.golang, o cliente MQTT 5 do Eclipse Paho para Go. Você passa uma lista de URLs de broker e alguns callbacks; ele conecta, reconecta com backoff quando a rede cai e avisa você toda vez que uma conexão é (re)estabelecida. Este tutorial monta um subscriber e um publisher completos com ele, adiciona credenciais e TLS e mostra como testar a reconexão.
Se você só precisa de MQTT 3.1.1, o cliente paho.mqtt.golang continua ótimo. Escolha o autopaho quando quiser recursos do MQTT 5 — reason codes, expiração de sessão, user properties, expiração de mensagens — junto com reconexão automática.
paho vs autopaho
O módulo tem duas camadas, e vale saber qual delas você está chamando:
| Pacote | O que faz | Reconecta? |
|---|---|---|
paho | Uma conexão MQTT 5 sobre um net.Conn fornecido por você. Os tipos de pacote (paho.Publish, paho.Subscribe) ficam aqui. | Não |
autopaho | Disca para o broker, encapsula um paho.Client, tenta de novo em caso de falha e expõe Publish, Subscribe, AwaitConnection | Sim |
Na prática você configura autopaho.ClientConfig, que embute um paho.ClientConfig para o client ID e os callbacks de mensagem, e monta os pacotes de requisição com o pacote paho.
Instalação
mkdir autopaho-demo && cd autopaho-demo
go mod init example.com/autopaho-demo
go get github.com/eclipse/paho.golang@latestOs dois pacotes vêm do mesmo módulo, então um único go get basta. Use uma versão recente do Go; a biblioteca acompanha as versões atuais.
Um cliente completo
Este programa se inscreve em um topic, publica uma mensagem por segundo e encerra de forma limpa com Ctrl+C. Ele roda contra o endpoint público de testes broker.connectmq.com; troque o host pelo seu próprio broker quando estiver pronto.
package main
import (
"context"
"fmt"
"log"
"net/url"
"os"
"os/signal"
"syscall"
"time"
"github.com/eclipse/paho.golang/autopaho"
"github.com/eclipse/paho.golang/paho"
)
const topic = "testmqtt/go-autopaho/demo"
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
u, err := url.Parse("mqtt://broker.connectmq.com:1883")
if err != nil {
log.Fatal(err)
}
cfg := autopaho.ClientConfig{
ServerUrls: []*url.URL{u},
KeepAlive: 30,
CleanStartOnInitialConnection: true,
SessionExpiryInterval: 0,
OnConnectionUp: func(cm *autopaho.ConnectionManager, ack *paho.Connack) {
log.Println("conectado, fazendo subscribe")
if _, err := cm.Subscribe(context.Background(), &paho.Subscribe{
Subscriptions: []paho.SubscribeOptions{{Topic: topic, QoS: 1}},
}); err != nil {
log.Printf("falha no subscribe: %v", err)
}
},
OnConnectError: func(err error) { log.Printf("erro de conexão: %v", err) },
ClientConfig: paho.ClientConfig{
ClientID: "testmqtt-autopaho-demo",
OnPublishReceived: []func(paho.PublishReceived) (bool, error){
func(pr paho.PublishReceived) (bool, error) {
log.Printf("recebido %s: %s", pr.Packet.Topic, pr.Packet.Payload)
return true, nil
},
},
OnClientError: func(err error) { log.Printf("erro do cliente: %v", err) },
OnServerDisconnect: func(d *paho.Disconnect) {
log.Printf("desconexão pelo servidor, reason code %d", d.ReasonCode)
},
},
}
cm, err := autopaho.NewConnection(ctx, cfg)
if err != nil {
log.Fatal(err)
}
if err := cm.AwaitConnection(ctx); err != nil {
log.Fatal(err)
}
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for i := 0; ; i++ {
select {
case <-ticker.C:
_, err := cm.Publish(ctx, &paho.Publish{
Topic: topic,
QoS: 1,
Payload: []byte(fmt.Sprintf("message %d", i)),
})
if err != nil {
log.Printf("falha no publish: %v", err)
}
case <-ctx.Done():
dctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = cm.Disconnect(dctx)
<-cm.Done()
return
}
}
}Rode com go run . e você verá uma linha de recebimento para cada mensagem publicada. Para ver o mesmo tráfego de fora, abra o cliente MQTT online, conecte em broker.connectmq.com e se inscreva em testmqtt/go-autopaho/#.
Alguns detalhes importam:
NewConnectionretorna na hora. Ele começa a conectar em segundo plano;AwaitConnectionbloqueia até a primeira conexão dar certo ou o contexto ser cancelado.- Faça o subscribe no
OnConnectionUp. Esse callback dispara na primeira conexão e em cada reconexão, então suas inscrições voltam sozinhas depois de uma queda de rede. OnPublishReceivedé um slice. Cada handler retorna(handled bool, err error); vários handlers podem inspecionar a mesma mensagem, por exemplo um por prefixo de topic.Publishcom QoS 1 espera o PUBACK e retorna um*paho.PublishResponsecom o reason code do broker — é assim que o MQTT 5 reporta problemas como "not authorized" em um publish específico.
Credenciais e TLS
As versões recentes do autopaho recebem as credenciais direto na configuração. Para TLS, mude o esquema da URL para mqtts:// e a porta para 8883; a biblioteca padrão do Go verifica o certificado contra as raízes do sistema.
u, _ := url.Parse("mqtts://broker.connectmq.com:8883")
cfg := autopaho.ClientConfig{
ServerUrls: []*url.URL{u},
KeepAlive: 30,
ConnectUsername: os.Getenv("MQTT_USER"),
ConnectPassword: []byte(os.Getenv("MQTT_PASS")),
// Só é necessário para uma CA privada ou certificados de cliente:
// TlsCfg: &tls.Config{RootCAs: pool, Certificates: []tls.Certificate{cert}},
ClientConfig: paho.ClientConfig{ClientID: "testmqtt-autopaho-tls"},
}ServerUrls é uma lista: o autopaho tenta as URLs em ordem, o que dá um failover simples entre dois endpoints. URLs ws:// e wss:// (por exemplo wss://broker.connectmq.com:8084/mqtt) também são aceitas quando as portas TCP estão bloqueadas. Se o handshake falhar, o guia de teste de conexão TLS mostra como verificar a cadeia de certificados com openssl s_client.
Sessões persistentes em vez de nova inscrição
Fazer o subscribe no OnConnectionUp é simples e robusto. A alternativa é deixar o broker lembrar de você:
cfg.CleanStartOnInitialConnection = false
cfg.SessionExpiryInterval = 3600 // segundosO endpoint de testes compartilhado não guarda sessões offline, então teste esta parte contra a sua própria instância privada de broker.
Com um client ID fixo, o broker guarda as inscrições e as mensagens QoS 1/2 enfileiradas por até uma hora após uma desconexão. O campo ack.SessionPresent no OnConnectionUp informa se a sessão sobreviveu, para você pular o subscribe nesse caso. A expiração de sessão é um recurso do MQTT 5; veja sessões persistentes para entender como os brokers lidam com isso.
Para mensagens de saída que precisam sobreviver a uma desconexão, o autopaho também oferece PublishViaQueue, que guarda as mensagens em uma fila (em memória por padrão, ou em disco com o pacote de fila baseado em arquivos) e as envia quando a conexão volta.
Comportamento de reconexão
O autopaho tenta de novo as conexões que falham, com um intervalo entre as tentativas. Versões recentes expõem isso como ReconnectBackoff na configuração (as antigas usavam um ConnectRetryDelay fixo); o padrão é sensato para a maioria dos clientes. Se você opera uma frota grande, garanta que os clientes não reconectem todos no mesmo instante depois de um restart do broker — adicione jitter ou um backoff crescente, como em boas práticas de reconexão.
As tentativas que falham aparecem no OnConnectError. Mensagens típicas e o que significam:
| O que você vê | Causa provável |
|---|---|
dial tcp ...: i/o timeout | Porta bloqueada por firewall ou host errado |
connection refused | Nada escutando na porta, p. ex. URL TLS numa porta sem TLS |
x509: certificate signed by unknown authority | CA privada ausente em TlsCfg.RootCAs |
| Reason code 134 / 135 no CONNACK | Usuário ou senha incorretos / não autorizado |
Consulte qualquer código numérico na referência de reason codes MQTT.
Testando a reconexão
Não espere a produção para descobrir se a nova inscrição funciona. Uma verificação manual rápida:
- Rode o programa e confirme que as mensagens estão fluindo.
- Corte a rede (desligue o Wi-Fi, ou
sudo ifconfig en0 down/sudo ip link set eth0 down) por mais tempo que o keep-alive. - Veja o
OnConnectErrorregistrar as tentativas que falham. - Restaure a rede. O
OnConnectionUpdeve registrar "conectado, fazendo subscribe" e as linhas de recebimento devem voltar.
Para testes automatizados, envolva o ciclo publicar/receber em go test com timeout — testando clientes MQTT em Go traz um helper pronto para o autopaho.
Acompanhe seu cliente autopaho no navegador
O autopaho dá aos programas Go um cliente MQTT 5 com cara de produção usando pouquíssimo código: configure URLs e callbacks, faça o subscribe no OnConnectionUp e confira os reason codes que ele devolve. Teste um corte de rede uma vez, mantenha os client IDs únicos e você terá um cliente que se recupera sozinho.
Perguntas frequentes
Qual a diferença entre paho.mqtt.golang e paho.golang?
github.com/eclipse/paho.mqtt.golang é o cliente antigo e suporta apenas MQTT 3.1.1. github.com/eclipse/paho.golang é uma implementação nova de MQTT 5: o pacote paho é um cliente de baixo nível com uma única conexão e o pacote autopaho adiciona gerenciamento de conexão e reconexão automática.
O autopaho refaz as inscrições depois de reconectar?
Não sozinho. Faça o subscribe dentro do OnConnectionUp para que cada nova conexão se inscreva de novo, ou conecte com CleanStartOnInitialConnection false e um SessionExpiryInterval para o broker manter a sessão e as inscrições.
Como uso TLS com o autopaho?
Use uma URL mqtts:// (ou ssl://, tls://) na porta 8883 e, se precisar de uma CA própria ou certificado de cliente, defina TlsCfg em autopaho.ClientConfig com um *tls.Config. Para transporte WebSocket use wss:// na porta 8084 com o caminho /mqtt.

