MQTT简介
MQTT(消息队列遥测传输)是ISO 标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议。它工作在 TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议,为此,它需要一个消息中间件 。
MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。
Moquette简介
Moquette是用Java基于Netty实现的轻量级Mqtt Broker,性能上不用太担心。
整合到SpringBoot
1. 新建一个SpringBoot项目,通过Maven引入Moquette。
声明Maven仓库:
<repositories>
<repository>
<id>bintray</id>
<url>https://jcenter.bintray.com</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
引入依赖
<dependency>
<groupId>io.moquette</groupId>
<artifactId>moquette-broker</artifactId>
<version>0.15</version>
</dependency>
2. 自定义MoquetteServer包装类,实现start和stop方法。
package com.subsliu.mqttbroker.server;
import io.moquette.broker.Server;
import io.moquette.broker.config.ClasspathResourceLoader;
import io.moquette.broker.config.IConfig;
import io.moquette.broker.config.IResourceLoader;
import io.moquette.broker.config.ResourceLoaderConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
/**
* @author Lzx
* @version 1.0
* @date 2022/4/26
*/
@Service
public class MoquetteServer {
private static final Logger LOGGER = LoggerFactory.getLogger(MoquetteServer.class);
private Server mqttServer;
/**
* 启动 MoquetteServer
*/
public void start() {
// 初始化配置文件
IResourceLoader configFileResourceLoader = new ClasspathResourceLoader("mqtt/moquette-config.conf");
final IConfig config = new ResourceLoaderConfig(configFileResourceLoader);
mqttServer = new Server();
// 启动服务
mqttServer.startServer(config, null, null, null, null);
LOGGER.info("Moquette Server Started.");
}
/**
* 关闭 MoquetteServer
*/
public void stop() {
if (mqttServer != null) {
mqttServer.stopServer();
LOGGER.info("Moquette Server Stopped.");
}
}
}
3. 通过启动类启动 MoquetteServer。
package com.subsliu.mqttbroker;
import com.subsliu.mqttbroker.server.MoquetteServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class MqttBrokerApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MqttBrokerApplication.class);
final ApplicationContext context = application.run(args);
// 启动 MoquetteServer
MoquetteServer server = context.getBean(MoquetteServer.class);
server.start();
// 服务关闭时,同时关闭 MoquetteServer
Runtime.getRuntime().addShutdownHook(new Thread(server::stop));
}
}
服务端测试
上面已经写好了一个简单、裸奔的MQTT服务端,下面开始测试它。
新开一个SpringBoot项目,并引入mqtt客户端maven依赖,这里我选择的是eclipse的Paho,点这里跳转官方文档。
制定Maven仓库:
<repositories>
<repository>
<id>Eclipse Paho Repo</id>
<url>https://repo.eclipse.org/content/repositories/paho-releases/</url>
</repository>
</repositories>
引入依赖:
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>
编写生产者,测试消息发布
package com.subsliu.mqttclient.publisher;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Lzx
* @version 1.0
* @date 2022/4/26
*/
public class MqttPublisher {
private static final Logger LOGGER = LoggerFactory.getLogger(MqttPublisher.class);
public static void main(String[] args) {
// 消息主题
String topic = "TOPIC_TEST";
// 消息内容
String content = "测试消息";
// 消息质量级别
int qos = 1;
// mqtt broker 连接地址
String broker = "tcp://127.0.0.1:1883";
// 客户端ID
String clientId = "clientId";
MemoryPersistence persistence = new MemoryPersistence();
// 初始化mqtt客户端,并连接服务端
try(MqttClient client = new MqttClient(broker, clientId, persistence);) {
MqttConnectOptions connOpts = new MqttConnectOptions();
connOpts.setCleanSession(true);
LOGGER.info("Connecting to broker: {}", broker);
client.connect(connOpts);
LOGGER.info("Connected");
// 发送消息
LOGGER.info("Publishing message: {}", content);
MqttMessage message = new MqttMessage(content.getBytes());
message.setQos(qos);
client.publish(topic, message);
LOGGER.info("Message published");
// 断开连接
client.disconnect();
LOGGER.info("Disconnected");
} catch(MqttException me) {
String message = String.format("code: %s, msg: %s", me.getReasonCode(), me.getMessage());
LOGGER.error(message, me);
}
}
}
编写消费者,测试消息订阅接收
package com.subsliu.mqttclient.subscriber;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
/**
* @author Lzx
* @version 1.0
* @date 2022/4/26
*/
public class MqttSubscriber {
private static final Logger LOGGER = LoggerFactory.getLogger(MqttSubscriber.class);
public static void main(String[] args) {
// 消息主题
String topic = "TOPIC_TEST";
// mqtt broker 连接地址
String broker = "tcp://127.0.0.1:1883";
// 客户端ID
String clientId = "subscriber-001";
MemoryPersistence persistence = new MemoryPersistence();
// 初始化mqtt客户端,并连接服务端
try (MqttClient client = new MqttClient(broker, clientId, persistence)){
MqttConnectOptions connOpts = new MqttConnectOptions();
connOpts.setCleanSession(true);
connOpts.setAutomaticReconnect(true);
LOGGER.info("Connecting to broker: {}", broker);
client.connect(connOpts);
LOGGER.info("Connected");
// 订阅消息
LOGGER.info("Subscribe topic: {}", topic);
client.subscribe(topic);
// 注册callback
client.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable throwable) {
LOGGER.info("失去连接, 开始重连...");
try {
client.reconnect();
} catch (MqttException e) {
LOGGER.info("重连失败");
e.printStackTrace();
}
}
@Override
public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
LOGGER.info("收到新消息, topic: {}, message: {}", s , mqttMessage);
}
@Override
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
LOGGER.info("deliveryComplete");
}
});
// 等待
try {
TimeUnit.DAYS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch(MqttException me) {
String message = String.format("code: %s, msg: %s", me.getReasonCode(), me.getMessage());
LOGGER.error(message, me);
}
}
}