Cliente MQTT em Java
1. Visão geral
Neste tutorial, veremos como podemos adicionar mensagens MQTT em um projeto Java usando as bibliotecas fornecidas porEclipse Paho project.
2. MQTT Primer
MQTT (MQ Telemetry Transport) is a messaging protocol que foi criado para atender à necessidade de um método simples e leve para transferir dados de / para dispositivos de baixa potência, como aqueles usados em aplicações industriais.
Com a crescente popularidade dos dispositivos IoT (Internet das Coisas), o MQTT viu um uso aumentado, levando à sua padronização pelo OASIS e ISO.
O protocolo suporta um único padrão de sistema de mensagens, ou seja, o padrão Publicar-Assinar: cada mensagem enviada por um cliente contém um "tópico" associado que é usado pelo broker para rotear para clientes assinados. Os nomes dos tópicos podem ser strings simples como “oiltemp” ou uma string semelhante a um caminho “motor/1/rpm“.
Para receber mensagens, um cliente assina um ou mais tópicos usando seu nome exato ou uma sequência que contém um dos curingas suportados ("#" para tópicos de vários níveis e "+" para nível único ").
3. Configuração do Projeto
Para incluir a biblioteca Paho em um projeto Maven, precisamos adicionar a seguinte dependência:
org.eclipse.paho
org.eclipse.paho.client.mqttv3
1.2.0
A versão mais recente do módulo da biblioteca JavaEclipse Paho pode ser baixada do Maven Central.
4. Configuração do cliente
Ao usar a biblioteca Paho, a primeira coisa que precisamos fazer para enviar e / ou receber mensagens de um broker MQTT éobtain an implementation of the IMqttClient interface. . Esta interface contém todos os métodos exigidos por um aplicativo para estabelecer uma conexão com o servidor, enviar e receber mensagens.
Paho sai da caixa com duas implementações desta interface, uma assíncrona (MqttAsyncClient) e uma síncrona (MqttClient). No nosso caso, vamos nos concentrar na síncrona versão, que tem semântica mais simples.
A configuração em si é um processo de duas etapas: primeiro criamos uma instância da classeMqttClient e depois a conectamos ao nosso servidor. A subseção a seguir detalha essas etapas.
4.1. Criação de uma nova instânciaIMqttClient
O seguinte snippet de código mostra como criar uma nova instância síncronaIMqttClient:
String publisherId = UUID.randomUUID().toString();
IMqttClient publisher = new MqttClient("tcp://iot.eclipse.org:1883",publisherId);
Neste caso,we’re using the simplest constructor available, which takes the endpoint address of our MQTT broker and a client identifier, que identifica o nosso cliente de forma única.
No nosso caso, usamos um UUID aleatório, para que um novo identificador de cliente seja gerado a cada execução.
Paho também fornece construtores adicionais que podemos usar para personalizar o mecanismo de persistência usado para armazenar mensagens não reconhecidas e / ouScheduledExecutorService usados para executar tarefas em segundo plano exigidas pela implementação do mecanismo de protocolo.
The server endpoint we’re using is a public MQTT broker hosted by the Paho project, que permite que qualquer pessoa com uma conexão à Internet teste clientes sem a necessidade de qualquer autenticação.
4.2. Conectando ao servidor
Nossa instânciaMqttClient recém-criada não está conectada ao servidor. We do so by calling its connect() method, opcionalmente passando uma posiçãoMqttConnectOptions que nos permite customizar alguns aspectos do protocolo.
Em particular, podemos usar essas opções para passar informações adicionais, como credenciais de segurança, modo de recuperação de sessão, modo de reconexão e assim por diante.
A classeMqttConnectionOptions expõe essas opções como propriedades simples que podemos definir usando métodos de definição normais. Nós só precisamos definir as propriedades necessárias para o nosso cenário - as demais assumirão os valores padrão.
O código usado para estabelecer uma conexão com o servidor normalmente se parece com isso:
MqttConnectOptions options = new MqttConnectOptions();
options.setAutomaticReconnect(true);
options.setCleanSession(true);
options.setConnectionTimeout(10);
publisher.connect(options);
Aqui, definimos nossas opções de conexão para que:
-
A biblioteca tentará se reconectar automaticamente ao servidor no caso de uma falha na rede
-
Ele descartará mensagens não enviadas de uma execução anterior
-
O tempo limite da conexão está definido para 10 segundos
5. Enviando Mensagens
O envio de mensagens usando umMqttClient já conectado é muito simples. We use one of the publish() method variants to send the payload, which is always a byte array, to a given topic, usando uma das seguintes opções de qualidade de serviço:
-
0 - semântica “no máximo uma vez”, também conhecida como “atire e esqueça”. Use esta opção quando a perda de mensagens for aceitável, pois não requer nenhum tipo de reconhecimento ou persistência
-
1 - semântica “pelo menos uma vez”. Use esta opção quando a perda de mensagens não for aceitáveland seus assinantes podem lidar com duplicatas
-
2 - semântica “exatamente uma vez”. Use esta opção quando a perda de mensagens não for aceitáveland seus assinantes não podem lidar com duplicatas
Em nosso projeto de exemplo, oEngineTemperatureSensor class desempenha o papel de um sensor simulado que produz uma nova leitura de temperatura sempre que invocamos seu métodocall().
Esta classe implementa a interfaceCallable para que possamos usá-la facilmente com uma das implementaçõesExecutorService disponíveis no pacotejava.util.concurrent:
public class EngineTemperatureSensor implements Callable {
// ... private members omitted
public EngineTemperatureSensor(IMqttClient client) {
this.client = client;
}
@Override
public Void call() throws Exception {
if ( !client.isConnected()) {
return null;
}
MqttMessage msg = readEngineTemp();
msg.setQos(0);
msg.setRetained(true);
client.publish(TOPIC,msg);
return null;
}
private MqttMessage readEngineTemp() {
double temp = 80 + rnd.nextDouble() * 20.0;
byte[] payload = String.format("T:%04.2f",temp)
.getBytes();
return new MqttMessage(payload);
}
}
The MqttMessage encapsulates the payload itself, the requested Quality-of-Service and also the retained flag for the message. Este sinalizador indica ao corretor que ele deve reter esta mensagem até que seja consumida por um assinante.
Podemos usar esse recurso para implementar um comportamento de "último bem conhecido"; portanto, quando um novo assinante se conectar ao servidor, ele receberá a mensagem retida imediatamente.
6. Recebendo Mensagens
Para receber mensagens do corretor MQTT,we need to use one of the subscribe() method variants, que nos permitem especificar:
-
Um ou mais filtros de tópicos para mensagens que queremos receber
-
A QoS associada
-
O manipulador de retorno de chamada para processar as mensagens recebidas
No exemplo a seguir, mostramos como adicionar um ouvinte de mensagem a uma instânciaIMqttClient existente para receber mensagens de um determinado tópico. UsamosCountDownLatch como um mecanismo de sincronização entre nosso retorno de chamada e o thread de execução principal, diminuindo-o toda vez que uma nova mensagem chega.
No código de amostra, usamos uma instânciaIMqttClient diferente para receber mensagens. Fizemos isso apenas para deixar mais claro qual cliente faz o quê, mas isso não é uma limitação da Paho - se você quiser, pode usar o mesmo cliente para publicar e receber mensagens:
CountDownLatch receivedSignal = new CountDownLatch(10);
subscriber.subscribe(EngineTemperatureSensor.TOPIC, (topic, msg) -> {
byte[] payload = msg.getPayload();
// ... payload handling omitted
receivedSignal.countDown();
});
receivedSignal.await(1, TimeUnit.MINUTES);
A variantesubscribe() usada acima leva uma instânciaIMqttMessageListener como seu segundo argumento.
No nosso caso, usamos uma função lambda simples que processa a carga útil e diminui um contador. Se não chegarem mensagens suficientes na janela de tempo especificada (1 minuto), o métodoawait() lançará uma exceção.
Ao usar o Paho, não precisamos confirmar explicitamente o recebimento da mensagem. Se o retorno de chamada retornar normalmente, o Paho assume um consumo bem-sucedido e envia uma confirmação ao servidor.
Se o retorno de chamada lançar umException, o cliente será encerrado. Please note that this will result in loss of any messages sent with QoS level of 0.
As mensagens enviadas com o nível 1 ou 2 de QoS serão reenviadas pelo servidor assim que o cliente for reconectado e se inscrever novamente no tópico.
7. Conclusão
Neste artigo, demonstramos como podemos incluir suporte para o protocolo MQTT em nossos aplicativos Java usando a biblioteca fornecida pelo projeto Eclipse Paho.
Essa biblioteca lida com todos os detalhes de protocolo de baixo nível, permitindo que nos concentremos em outros aspectos de nossa solução, deixando um bom espaço para personalizar aspectos importantes de seus recursos internos, como a persistência de mensagens.
O código mostrado neste artigo está disponívelover on GitHub.