Eclipse Mosquitto e GraalVM: Broker MQTT e Java Nativo para IoT
Este é meu primeiro post sobre o Eclipse Mosquitto, um broker MQTT open source amplamente utilizado em projetos IoT (Internet das Coisas). Vamos ver, passo a passo, como configurá-lo, executá-lo via Docker e Docker Compose, além de compilar nossas aplicações Java para binários nativos usando GraalVM, tudo para mostrar a agilidade e o baixo consumo de recursos que esse ecossistema pode oferecer.
O que é MQTT?
O MQTT é um protocolo de comunicação leve, ideal para dispositivos com recursos limitados de rede, processamento e consumo de energia. Ele usa o modelo publicador/assinante (pub/sub), onde:
-
O publicador (publisher) envia mensagens para um tópico;
-
O assinante (subscriber) se inscreve (subscribe) nesse tópico;
-
O broker (no nosso caso, o Eclipse Mosquitto) é responsável por intermediar essa comunicação, recebendo e entregando as mensagens.
Outras alternativas de brokers MQTT
Se você está pesquisando sobre brokers MQTT, saiba que o Eclipse Mosquitto não é o único nome na jogada. Há várias outras soluções, cada qual com seus diferenciais:
-
HiveMQ: Oferece um broker escalável para grandes infraestruturas IoT, com foco em alto desempenho e suporte a clustering.
-
EMQX: Uma plataforma MQTT que inclui recursos avançados de monitoramento e gerenciamento, além de integrar com outros protocolos.
-
VerneMQ: Conhecido por ser escrito em Erlang/OTP, oferece alta disponibilidade e escalabilidade por padrão.
-
RabbitMQ (com plugin MQTT): Embora o RabbitMQ seja mais conhecido como um broker AMQP, ele pode atuar como broker MQTT por meio de plugins — útil se você já usa RabbitMQ e quer unificar protocolos.
A escolha depende muito das suas necessidades de escalabilidade, linguagens envolvidas, requisitos de segurança, facilidade de configuração, entre outros fatores.
Por que o Mosquitto é uma das melhores alternativas?
O Eclipse Mosquitto brilha em cenários em que simplicidade, leveza e conformidade com o padrão MQTT são fundamentais. Alguns motivos que fazem dele uma escolha popular:
-
Instalação fácil: Disponível em repositórios oficiais de muitas distribuições Linux, em pacotes para Windows/macOS e em imagens Docker.
-
Leveza: Consome poucos recursos de CPU e memória, tornando-o ideal para rodar em dispositivos de borda (edge computing).
-
Open Source e confiável: Com ampla comunidade, manutenções constantes e suporte oficial da Eclipse Foundation.
-
Escalabilidade: Apesar de ser leve, pode lidar com um grande número de conexões simultâneas, desde que a infraestrutura de rede e hardware suporte.
-
Documentação clara: Fácil de aprender e configurar para quem está iniciando no mundo MQTT.
Onde o Mosquitto é utilizado em IoT
O Mosquitto se encaixa muito bem em projetos de:
-
Automação residencial: Dispositivos de casas inteligentes (smart homes) usam MQTT para trocar dados de sensores, lâmpadas, fechaduras, etc.
-
Sistemas de monitoramento: Sensores industriais ou ambientais (temperatura, umidade, vibração) publicam dados em tempo real para dashboards ou sistemas de alerta.
-
Veículos conectados: Alguns projetos de mobilidade, carros e drones utilizam MQTT para telemetria leve e confiável.
-
Smart Cities: Gerenciamento de iluminação pública, tráfego, coleta de lixo, onde há milhares de sensores trocando informações.
-
Dispositivos embarcados: Graças ao baixo overhead do protocolo, é comum em dispositivos com hardware limitado, tornando a comunicação eficiente.
Em resumo, sempre que você precisa de um broker confiável, compatível com o protocolo MQTT, e que possa rodar em ambientes com recursos limitados ou crescer para atender mais dispositivos, o Mosquitto surge como uma ótima escolha.
Subindo o Mosquitto com Docker e Docker Compose
1) Arquivo docker-compose.yml
Para facilitar a vida, podemos subir o Mosquitto com Docker Compose em vez de usar apenas docker run. Num diretório, crie dois arquivos: docker-compose.yml e mosquitto.conf. A estrutura final fica assim:
. ├── docker-compose.yml └── mosquitto.conf
docker-compose.yml (exemplo mínimo):
version: '3'
services:
mosquitto:
image: eclipse-mosquitto
container_name: mosquitto
ports:
- "1883:1883"
- "9001:9001"
volumes:
- "./mosquitto.conf:/mosquitto/config/mosquitto.conf"
restart: unless-stopped
2) Arquivo mosquitto.conf
# Escuta na porta 1883 para conexões TCP MQTT listener 1883 allow_anonymous true # Escuta na porta 9001 para conexões WebSocket listener 9001 protocol websockets allow_anonymous true
Em ambiente de produção, recomendamos desabilitar allow_anonymous true e configurar autenticação (user/password) ou ACLs (listas de controle de acesso).
3) Subindo o broker
No mesmo diretório, rode:
docker-compose up -d
Isso vai levantar o contêiner “mosquitto” em modo daemon, expondo as portas 1883 (TCP) e 9001 (WebSocket).
Atenção ao “local only mode” no Mosquitto 2.x
A partir da versão 2.x, o Mosquitto inicia em modo apenas local (“local only mode”) por padrão, o que impede conexões externas sem um listener configurado. O nosso mosquitto.conf acima já resolve isso, pois define explicitamente os listeners para 1883 e 9001.
Exemplo de Aplicação Java (Publisher + Subscriber)
Para nosso exemplo, vamos usar a Eclipse Paho, biblioteca oficial para clientes MQTT. E, para ficar ainda mais interessante, vamos mostrar como gerar um binário nativo com GraalVM, melhorando significativamente o tempo de inicialização (startup) e reduzindo o consumo de memória, algo crucial em cenários de IoT.
Dependências e Plugin GraalVM no Maven
A seguir, um exemplo de pom.xml que gera executáveis nativos tanto para o Publisher quanto para o Subscriber. Observe que usamos as propriedades mainClass e imageName para tornar o nome do binário e a classe principal parametrizáveis:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 \
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gasparbarancelli</groupId>
<artifactId>demo-eclipse-mosquitto</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>23</maven.compiler.source>
<maven.compiler.target>23</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Configurações default para gerar o Publisher -->
<imageName>publisher</imageName>
<mainClass>com.gasparbarancelli.Publisher</mainClass>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>0.10.4</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>${mainClass}</mainClass>
<imageName>${imageName}</imageName>
</configuration>
</plugin>
</plugins>
</build>
</project>
Código do Publisher
package com.gasparbarancelli;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
public class Publisher {
private static final String BROKER_URL = "tcp://localhost:1883";
private static final String TOPIC = "test/topic";
private static final String CLIENT_ID = "PublisherExample";
public static void main(String[] args) {
try {
MqttClient client = new MqttClient(BROKER_URL, CLIENT_ID, new MemoryPersistence());
client.connect();
// Se houver argumentos na linha de comando, vamos publicar a string deles
String mensagem = args.length > 0 ? String.join(" ", args)
: "Olá, Mosquitto! Mensagem de teste via MQTT.";
MqttMessage mqttMessage = new MqttMessage(mensagem.getBytes());
mqttMessage.setQos(1);
client.publish(TOPIC, mqttMessage);
System.out.println("Mensagem publicada: " + mensagem);
client.disconnect();
client.close();
} catch (MqttException e) {
e.printStackTrace();
}
}
}
Código do Subscriber
package com.gasparbarancelli;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
public class Subscriber implements MqttCallback {
private static final String BROKER_URL = "tcp://localhost:1883";
private static final String TOPIC = "test/topic";
private static final String CLIENT_ID = "SubscriberExample";
public static void main(String[] args) {
new Subscriber().startSubscriber();
}
public void startSubscriber() {
try {
MqttClient client = new MqttClient(BROKER_URL, CLIENT_ID, new MemoryPersistence());
client.setCallback(this);
client.connect();
client.subscribe(TOPIC, 1);
System.out.println("Assinado no tópico: " + TOPIC);
} catch (MqttException e) {
e.printStackTrace();
}
}
@Override
public void connectionLost(Throwable cause) {
System.out.println("Conexão perdida! Causa: " + cause.getMessage());
}
@Override
public void messageArrived(String topic, MqttMessage message) {
System.out.println("Mensagem recebida. Tópico: " + topic + ", Mensagem: " + new String(message.getPayload()));
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
// Chamado quando a publicação é confirmada
}
}
Gerando os binários nativos
1) Publisher
-
Se você quiser gerar o Publisher como binário, certifique-se de que o
pom.xmlaponte omainClasse oimageNamepara o Publisher. Exemplo:
<properties>
<imageName>publisher</imageName>
<mainClass>com.gasparbarancelli.Publisher</mainClass>
...
</properties>
-
Então, execute:
mvn clean package -Pnative
-
Será gerado um binário chamado publisher (no diretório
target/). -
Para publicar uma mensagem:
./target/publisher "Olá, Mosquitto! Mensagem de teste via MQTT."
2) Subscriber
Para gerar um binário do Subscriber, você pode sobrescrever as propriedades via linha de comando, sem precisar alterar o pom.xml:
mvn clean package -Pnative \ -DmainClass=com.gasparbarancelli.Subscriber \ -DimageName=subscriber
Isso gerará o binário subscriber em target/. Para rodar:
./target/subscriber
Baixo consumo de memória e prints
A grande vantagem aqui é que o binário nativo inicia rapidamente (em milissegundos) e tem um consumo muito menor de recursos se comparado a uma aplicação Java típica rodando em JVM. Veja os prints abaixo capturados em um MacBook Air, mostrando o subscriber rodando e assinando o tópico test/topic, além do top filtrado pelo PID. Note que o uso de memória do app nativo ficou abaixo de 5 MB (4577K):
Acima, o %CPU está em 0.0% (praticamente ocioso), e a MEM está em 4577K (~4,5 MB).
Abaixo, os logs mostrando as mensagens recebidas e publicadas:
Observe que o subscriber continua rodando, recebendo mensagens publicadas pelo publisher, que podem ser passadas como argumentos de linha de comando.
Código de Exemplo no GitHub
O repositório completo com todo o código de exemplo (Publisher, Subscriber e configuração) está disponível em:
Sinta-se à vontade para clonar e experimentar!
Conclusão
O Eclipse Mosquitto é uma solução simples e eficiente para quem quer ingressar no universo do MQTT e IoT sem complicações. Seu baixo consumo de recursos, aliado à facilidade de instalação e configuração (especialmente via Docker), o tornam uma excelente escolha para projetos que precisam de confiabilidade e escalabilidade.
Além disso, demonstramos como é fácil implementar um publisher e subscriber MQTT em Java usando a biblioteca Eclipse Paho, que segue rigorosamente as especificações do protocolo. E, ao compilar nossas aplicações para binários nativos com GraalVM, ganhamos em velocidade de inicialização e reduzimos significativamente o uso de memória, deixando a solução ainda mais atrativa para cenários de dispositivos embarcados e aplicações de alto desempenho.
Assim, para quem deseja combinar leveza, performance e praticidade, o Mosquitto continua sendo uma das escolhas favoritas de desenvolvedores e profissionais de IoT.