该教程将从mqtt的介绍——>优缺点——>linux centOS 7系统下安装部署mqtt——>并加入开机自启——>spring boot集成mqtt——>最后进行验证的全部流程。
目录
MQTT协议全称Meaage Queuing Telemetry Transport 消息队列遥测传输协议。
它是一种基于订阅/发布(Publish/Subscribe)模式的轻量级通讯协议,用于物联网(IoT)设备之间的通讯。该协议构建于TCP/IP协议之上,因TCP协议本身具有高可靠性特点,因此MQTT同样具有高可靠、低开销的特点,之所以低开销,是因为MQTT协议传输的最小报文只有两个字节。
MQTT协议有三种身份,发布者(Publish)、代理(Broker服务器)、订阅者(Subscribe)。发布者和订阅者都为客户端,消息代理为服务器,发布者也可以是订阅者。
二、专业术语
1.网络连接Network Connection
MQTT使用底层传输协议TCP基础设施,客户端使用它连接服务器,它提供有序的、可靠的、双向字节流传输。
2.应用消息Application Message
MQTT协议通过网络传输应用数据,应用消息通过MQTT传输时,需要有关联的服务质量(QoS)和主题(Topic)。
3.客户端Client
使用MQTT的程序或设备,客户端总是通过网络连接到服务器,它可以:
1)发布应用消息给其他相关的客户端。
2)订阅接收相关的应用消息。
3)取消订阅—移除接收应用消息的请求。
4)从服务端断开连接。
4.服务器Server
一个程序或设备,作为发送消息的客户端和请求订阅的客户端之间的中介。
1)接收来自客户端的网络连接。
2)接收客户端发布的应用消息。
3)处理客户端的订阅和取消订阅请求。
4)转发应用消息给符合条件的已订阅客户端。
5.订阅Subscribe
订阅包含一个主题过滤器(Topic Filter)和一个最大的服务质量(QoS)等级。订阅与单个会话(session)关联。会话可以包含多于一个的订阅。会话的每个订阅都有一个不同的主题过滤器。
6.主题名Topic Name
附加再应用消息上的一个标签,服务端已知且与订阅匹配。服务端发送应用消息的每一个副本给每一个匹配的客户端订阅。
7.主题过滤器Topic Filter
订阅中包含的一个表达式,用于表示相关的一个或多个主题。主题过滤器可以使用通配符。
8.会话session
客户端和服务端之间的状态交互。一些会话持续时长与网络连接一样,另一些可以在客户端和服务端的多个连续网络连接间扩展。
9.控制报文MQTT Control Packet
通过网络连接发送的信息数据包。MQTT规范定义了十四种不同类型的控制报文,其中一个(Publish报文)用于传输应用消息。
10.消息质量(QoS)
MQTT消息质量有三个等级,QoS 0、QoS 1和QoS 2。
QoS 0:最多分发一次。消息的传递完全依赖底层的TCP/IP网络,协议里没有定义答应和重试,消息要么只会到达服务端一次,要么根本没有到达。消息可能会丢失。
QoS 1:最少分发一次。服务器的消息接收由puback消息进行确认,如果通信链路或发送设备异常,或者指定时间内没有收到确认消息,发送端会重发这条再消息头中设置了DUP位的消息。
QoS 2:只分发一次。这是最高级别的消息传递,消息丢失和重复都是不可接受的,使用这个服务质量等级,会有额外的开销。例如,这个等级可用在一个计费系统中,这里如果消息重复或丢失会导致不正确的收费。
三、MQTT的优缺点
1.优点
轻量级:MQTT协议非常简单,头部只有2字节,开销小,适合网络带宽和内存资源有限的设备。
发布/订阅模式:MQTT采用发布/订阅模式,解除了发送方和接收方的耦合,适合处理大量的消息路由。
稳定性:MQTT支持断开重连,确保了网络的稳定性。
低延迟:MQTT采用心跳机制,可以保证消息的实时传输。
扩展性:MQTT支持Last Will和Testament特性,可以在客户端异常断开时通知其他客户端。
2.缺点
不支持复杂的数据类型:MQTT只支持字符串和二进制数据,不支持更复杂的数据类型。
不支持服务质量保证:虽然MQTT支持QoS,但是并没有在协议层面保证消息的顺序和不重复,需要应用层实现。
不支持服务发现:客户端需要知道服务端的地址,服务端的动态发现需要其他机制支持。
四、MQTT安装
版本信息:
系统:linux-centOS 7
mqtt版本:mosquitto-2.0.14.tar.gz
cJSON库:点击下载
1.上传mosquitto与cJSON到服务器/opt目录,并解压
[root@bogon /]# cd opt/
[root@bogon opt]# ll
drwxr-xr-x. 5 root root 4096 5月 16 16:13 cJSON
drwxrwxr-x. 20 1000 mysql 4096 5月 16 16:08 mosquitto-2.0.14
2.安装相关依赖与cJSON库
[root@bogon opt]# yum install -y c-ares-devel e2fsprogs-devel uuid-devel libuuid-devel
[root@bogon opt]# yum install libssl-dev libc-ares-dev uuid-dev daemon openssl-devel
[root@bogon opt]# cd cJSON
[root@bogon cJSON]# make && make install
3.安装mosquitto
[root@bogon cJSON]# cd ../mosquitto-2.0.14
[root@bogon mosquitto-2.0.14]# make && make install
4.修改mosquitto.conf配置文件
[root@bogon mosquitto-2.0.14]# vi mosquitto.conf
#加入以下配置
listener 1883 0.0.0.0 #允许所有电脑访问
allow_anonymous true #匿名登录,客户端连接不需要密码
5.加入开机自启
[root@bogon mosquitto-2.0.14]# vi /lib/systemd/system/mosquitto.service
#-------加入以下配置,然后保存----------
[Unit]
Description=Mosquitto MQTT Broker
After=network.target
[Service]
ExecStart=/usr/local/sbin/mosquitto -c /opt/mosquitto-2.0.14/mosquitto.conf
[Install]
WantedBy=multi-user.target
#---------------end--------------------
[root@bogon mosquitto-2.0.14]# systemctl daemon-reload #重新加载
[root@bogon mosquitto-2.0.14]# systemctl start mosquitto #启动mqtt服务
[root@bogon mosquitto-2.0.14]# systemctl enable mosquitto #加入开机自启
五、spring boot集成MQTT
1.新建spring boot项目,然后再pom.xml添加相关依赖包
<!-- spring boot 依赖包 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.18.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mqtt依赖包 -->
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
</dependency>
</dependencies>
2.application.yml增加配置
#MQTT配置
mqtt:
host: tcp://localhost:1883
userName: root
passWord: 123456
clientid: client1
timeout: 10
keepalive: 60
3.新建MyMqttCallback消息回调类
package com.example.demo.common.mqtt;
import com.example.demo.common.config.MqttConfiguration;
import org.eclipse.paho.client.mqttv3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* MQTT消息订阅回调类
*/
@Component
public class MyMqttCallback implements MqttCallback {
@Autowired
private MqttConfiguration mqttConfiguration;
/**
* MQTT连接中断,重连
*/
@Override
public void connectionLost(Throwable throwable) {
System.out.println("[connectionLost]方法,MQTT断开连接,发起重新连接");
mqttConfiguration.getMyMqttClient();
}
/**
* 接收订阅消息
* @param s
* @param MqttMessage
* @throws Exception
*/
@Override
public void messageArrived(String s, MqttMessage mqttMessage) {
System.out.println("收到消息主题:" + s + ", 消息内容:" + new String(MqttMessage.getPayload()));
}
/**
* 发送订阅消息成功后
* @param iMQTTDeliveryToken
*/
@Override
public void deliveryComplete(IMQTTDeliveryToken iMQTTDeliveryToken) {
System.out.println("deliveryComplete消息发送成功,处理业务逻辑");
}
}
4.新建MyMqttClient客户端类
package com.example.demo.common.mqtt;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* MQTT客户端
*/
@Component
public class MyMqttClient {
@Autowired
private MyMqttCallback myMqttCallback;
private MqttClient mqttClient;
/**
* 连接MQTT
* @param host
* @param ClientId
* @throws MQTTException
*/
public void connect(String username, String password, String host, String clientId, int timeout, int keepalive) {
//设置参数
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(username);
options.setPassword(password.toCharArray());
options.setConnectionTimeout(timeout);
options.setKeepAliveInterval(keepalive);
options.setCleanSession(true);
options.setAutomaticReconnect(true);
//启动项目连接失败,每2秒进行一次重连
while (true) {
try {
if (null == mqttClient) {
mqttClient = new MqttClient(host, clientId, new MemoryPersistence());
mqttClient.setCallback(myMqttCallback);
}
if (mqttClient.isConnected()) {
//连接成功,中断重连机制
break;
}
mqttClient.connect(options);
Thread.sleep(2000);
} catch (Exception e) {
System.out.println("[connect]方法,启动服务连接MQTT失败!");
e.printStackTrace();
}
}
System.out.println("[connect]方法,启动服务连接MQTT成功!");
}
/**
* 发布消息
*/
public void publish(String topic, String message) {
this.publish(topic, message, 0, false);
}
/**
* 发送消息
* @param Topic
* @param mesage
* @param QoS
* @param retained
*/
public void publish(String topic, String message, int qos, boolean retained) {
MqttMessage mqttMessage = new MqttMessage();
MqttMessage.setPayload(mesage.getBytes());
MqttMessage.setQos(qos);
MqttMessage.setRetained(retained);
try {
this.mqttClient.getTopic(topic).publish(mqttMessage);
} catch (MqttException e) {
System.out.println("[publish]方法,发送消息异常!");
e.printStackTrace();
}
}
/**
* 订阅
*/
public void subscribe(String topic, int qos) {
try {
mqttClient.subscribe(topic, qos);
} catch (MqttException e) {
System.out.println("[Subscribed]方法,订阅主题异常!");
e.printStackTrace();
}
}
/**
* 取消订阅
* @param Topic
*/
public void cleanTopic(String topic) {
try {
mqttClient.unsubscribe(topic);
} catch (MqttException e) {
System.out.println("[cleanTopic]方法,取消订阅主题异常!");
e.printStackTrace();
}
}
}
5.新建MqttConfiguration配置类
package com.example.demo.common.config;
import com.example.demo.common.mqtt.MyMqttClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* MQTT消息配置类
*/
@Component
public class MqttConfiguration {
@Autowired
private MyMqttClient myMqttClient;
@Value("${mqtt.host}")
String host;
@Value("${mqtt.username}")
String username;
@Value("${mqtt.password}")
String password;
@Value("${mqtt.clientId}")
String clientId;
@Value("${mqtt.timeout}")
int timeout;
@Value("${mqtt.keepalive}")
int keepalive;
@Bean
public MyMqttClient getMyMqttClient() {
try {
//连接服务器
myMqttClient.connect(username, password, host, clientId, timeout, keepalive);
//订阅相关主题
myMqttClient.subscribe("A/pick/warn/#", 1);
myMqttClient.subscribe("A/cmd/resp", 1);
myMqttClient.subscribe("ABCH", 1);
} catch (Exception e) {
e.printStackTrace();
}
return myMqttClient;
}
}
六、验证
1.编写测试类DemoController,并启动服务
package com.example.demo.controller;
import com.example.demo.common.config.mqtt.MyMqttClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@Autowired
private MyMqttClient myMqttClient;
@RequestMapping("/demo")
public boolean demo(@RequestParam("topic") String topic, @RequestParam("message") String mesage) {
myMqttClient.publish(topic, mesage);
return true;
}
}
2.浏览器访问测试接口,给A/cmd/resp队列发送aaaa消息
3.后端服务打印收到的消息