Eclipse Mosquitto e GraalVM: Broker MQTT e Java Nativo para IoT

Por Gaspar Barancelli Junior em 25 de dezembro de 2024
Imagem ilustrativa sobre o post 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

  1. Se você quiser gerar o Publisher como binário, certifique-se de que o pom.xml aponte o mainClass e o imageName para o Publisher. Exemplo:

<properties>
    <imageName>publisher</imageName>
    <mainClass>com.gasparbarancelli.Publisher</mainClass>
    ...
</properties>
  1. Então, execute:

mvn clean package -Pnative
  1. Será gerado um binário chamado publisher (no diretório target/).

  2. 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):

consumo memoria graalvm java apache mosquitto
Figure 1. top com subscriber consumindo ~4.5MB

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:

graalvm java apache mosquitto publisher
Figure 2. Logs de execução do Subscriber e Publisher
graalvm java apache mosquitto subscriber
Figure 3. Logs de execução do Subscriber e Publisher

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.

// Compartilhe esse Post

💫
🔥 NOVO APP

Domine o Inglês em 30 dias!

Inteligência Artificial + Repetição Espaçada • Método cientificamente comprovado

✅ Grátis para começar 🚀 Resultados rápidos
×