What???---mqtt是什么?
MQTT (Message Queuing Telemetry Transport),即消息队列遥测传输协议。是一种基于发布/订阅(Publish/Subscribe)模式的轻量级通讯协议,并且该协议构建于TCP/IP协议之上。MQTT协议具有轻量级、可靠性、灵活性、安全性、异步通信、支持多种消息传输模式以及有持续地会话感知能力等特点。这些特点使得MQTT在物联网、传感器网络、移动设备等领域得到广泛应用。
控制报文
MQTT的报文格式非常简洁,所以具有轻量级的特点。他们都由固定报头、可变报头和有效载荷三个部分组成。PINGREQ报文仅仅含有固定报头。

固定报头格式也有三个部分;
-
报文类型:标识报文是三大类型中的类型
-
标识位:在PUBLISH报文类型中,标识服务质量等级
-
剩余长度:记录报文的可变报头+有效载荷的字节大小。所以整个报文的长度=固定报头+剩余长度。
主要可以分为三大类控制报文:链接、发布、订阅。
Qos消息的质量等级
- Qos值 为 0 表示这个消息最多发送一次。 可能导致消息的丢失;
- Qos值 为 1 表示这个消息至少发送一次。 不会出现消息的丢失,但是可能会发生重复现象
- Qos值 为 2 表示这个消息仅有一次发送。 不会丢失也不会重复
消息质量等级在消息发布者上设置,如果订阅者设置了质量等级上限,以后者设置的等级为限。
Qos=0,发送方无需等待确认、存储、重发,所以不存在重复,但是可能因为网络因素等导致丢失。
Qos=1,发送方会先缓存这个PUBLISH报文,这个报文会携带一个packet ID,等待接收方(broker)的PUBACK报文并携带前者的packet ID。因为有应答机制所以消息不会丢失,但是如果返回的PUBACK报文因为网络因素等丢失、延迟,发送方会重传,就发生了重复。
Qos=2,可以保证消息既不丢失也不重复。保证不丢失的原因和qos=1的相同。
不重复是因为qos=2的条件下,可以根据packet ID进行去重,整个消息传输涉及四个报文类型:PUBLISH PUBREC PUBREL PUBCOMP。只有当发送方收到pubrec确认报文然后发送pubrel packetID释放报文,packetID才会进入释放状态,等收到pubcomp确认报文才会变成可用。
qos=1做不到根据packetID去重是因为消息一经发送它的packetID就释放(可用)了。
主题
主题主要对消息进行分类、映射,例如 topic-a------>message-X ,消息X的主题就是a,订阅者可以通过订阅a这个主题来获取消息X。
主题通过 ‘/’ 进行分层
topic/ttt/ppp/10
还可以对主题使用通配符‘+’、‘#’,方便订阅者一次订阅多个主题
tttt/+/dsdfsd 有效 ‘+’通配符必须占据一个层级
sfsdf+ 无效 这个格式就不符合上面的规定
+ 有效
getttab/+ 有效
------------------------------------------------
xyz/# 有效 ‘#’通配符必须占据一个层级而且必须是最后一个字符
xyz/123# 无效 虽然在最后,但是不符合占据一个层级
xyz/#/dfsdf 无效
消息
保留消息
保留消息通过客户端发送的时候,将retain属性选上。之后就可以在broker(emqx)上的保留消息上查看到。


保留消息会保留最新消息(即如果相同主题的保留消息发送了多条,在broker中只会保留最后发送的那一条)。消息还可以设置过期时间,控制它在broker上存活的时间、通过发送空的消息进行删除。
遗嘱消息
当一个客户端连接了服务端后可以,注册一个遗嘱消息,当这个客户端意外断开连接后,订阅相应主题的客户端就会收到这条遗嘱消息,从而得知意外断开的连接。
通过设置will delay interval 属性设置意外断开后延迟多久发送这个遗嘱消息,可以避免短时间的断开影响
延迟发布
将消息经过一段时间后再发送;通过设置主题格式实现
$delayed/延迟时间/正在的主题名
共享订阅
$share订阅是带群组的订阅,每个组都会发到,但是组内是共享订阅即只有一个客户端会消费这条消息
$share/组名/主题
$queue/主题
Why???---为什么使用
MQTT(Message Queuing Telemetry Transport)协议之所以被广泛使用,主要归因于其在多个方面的优势和特点,这些优势和特点使其特别适用于物联网(IoT)、移动应用、远程监控等场景。
一、轻量级与高效性
MQTT协议设计简洁,开销小,采用二进制协议,数据包小,传输效率高。这使得它非常适合在带宽较小的网络环境下使用,特别是在物联网场景中,许多设备都受到资源限制,如处理器能力、内存和闪存容量等。MQTT的轻量级特性使得这些设备能够高效地传输数据,同时减少能耗,延长设备的使用寿命。
二、灵活性与可靠性
MQTT协议支持多种消息质量等级(QoS),从最多一次传输(QoS 0)到确保消息至少传输一次(QoS 1)或仅传输一次(QoS 2),允许开发者根据应用需求选择消息传递的保证级别。这种灵活性使得MQTT能够适应不同的网络环境和应用需求。同时,MQTT还提供了消息持久化机制,确保消息能够可靠地传递给接收者,即使在设备离线或网络不稳定的情况下也能保证消息的传递。
三、异步通信与解耦
MQTT采用发布/订阅模式,允许消息的异步传递。发送者和接收者之间解耦,发送者只需将消息发布到特定的主题上,而无需关心哪些接收者订阅了该主题。这种解耦的通信模式提高了系统的可伸缩性和灵活性,使得系统能够轻松应对大量并发连接和消息传递。
四、设备感知与状态监测
MQTT支持设备的在线/离线状态监测,可以实时感知设备的连接状态变化。这对于物联网场景中的设备管理和维护至关重要,可以帮助开发者及时发现并处理设备故障或异常情况。
五、安全性与加密
MQTT协议支持通过TLS/SSL进行加密,以保护消息的传输安全。这确保了敏感数据在传输过程中不会被窃取或篡改,从而保证了系统的安全性和可靠性。
How???---基本使用
期间使用linux发生空间不足情况
之所以使用逻辑卷是因为方便高效管理硬盘分区,还能进行跨分区存取很方便。
linux的逻辑卷(lv)制作过程:物理硬盘(dev/)----->(将它分区或者不分区经过pvcreate变成)物理卷(pv)----->(通过vgextend 卷组名 pvName 加入)卷组(vg)---->从卷组中抽调空间,制作逻辑卷---->格式化--->挂载到某个目录就能进行使用。
逻辑卷扩展
lsblk -f # 查看所有分区以及相关物理卷、卷组、和逻辑卷
pvdisplay vgdisplay lvdisplay #查看物理卷、卷组、逻辑卷
# 如果想要扩展的逻辑卷的所在卷组还有空余空间,直接扩展
lvextend -L +5G /dev/vgName/lvName
# 如果卷组没有空余空间,需要先添加物理卷进入卷组
vgextend 目标卷组名 添加的物理卷名 然后再扩展逻辑卷
# 这个物理卷怎么来呢? 从硬盘(分区)来
pvcreate /dev/sda1.....
# 最后还要调整文件系统
resize2fs 目标逻辑卷名
# 还可以自己创建卷组、逻辑卷使用 默认卷组是centos
# **create **remove 实现增添、删除
正题:服务端broker(emqx)使用
主要都是在dashboard上进行设置操作
客户端认证
- 选择认证方式(password-based \ jwt \ scram)
- 数据源(内置)
- 配置参数
客户端授权
可以通过ACL文件进行授权
{权限(allow、deny),客户端(clientID/ip/username),操作(sub、pub、all),[主题集]}
broker检查连接的客户端是否具有访问某些数据的权限
黑名单
连接抖动
支持自动封禁那些被检测到短时间内频繁登录的客户端,并且在一段时间内拒绝这些客户端的登录,以避免此类客户端过多占用服务资源而影响其他客户端的正常使用。
只会封禁客户端ID,并不封禁用户名和ip地址。
数据集成
引入了与外部数据系统(物理设备传感器之流)的连接,从而实现设备与其他业务系统的无缝集成。
emqx的数据集成不单单可以快速将物联网设备产生的数据传递到业务系统中,也可以和其他外部数据系统集成,实现数据的快速传输。比如从kafka某个主题中获取数据,然后将数据写入redis中。
工作原理
- Sink 用于将消息发送到外部数据系统,例如mysql、kafka或http服务等。
- Source 用于从外部数据系统接受消息,例如MQTT、kafka。
- 连接器 : 连接器负责与外部数据系统的连接。
规则引擎
基于SQL的数据处理组件,有数据提取、过滤、转换、存储与处理。包含
数据来源----数据处理过程----处理结果去向
数据集成入门
在emqx中的集成下可以创建规则、连接器等,点进去根据指示创建即可。
正题:客户端MQTTX编码使用
集成mqtt客户端,<spring-integration-mqtt>
创建连接工厂
@Configuration
public class MqttConfiguration {
@Bean
public MqttPahoClientFactory mqttPahoClientFactory() {
DefaultMqttPahoClentFactory mqttPahoClientFactory = new DefaultMqttPahoClentFactory();
MqttConnectOptions options = new MqttConnectOptions();
optons.setUserName();
........
mqttPahoClientFactory.setConnectionOptions(options);
return mqttPahoClientFactory;
}
// 消息通道
@Bean
public MessageChannel messageInputChannel(){
return new DirectChannel();
}
// 订阅主题、设置消息的信息
@Bean
public MessageProducer mqttInbound(MessageChannel messageInputChannel, MqttPahoClientFactory mqttPahoClientFactory){
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter(url,clientid,factory,topic);
adapter.setCompletionTimeout(5000);
adapter.setConverter(new DefaultPahoMessageConverter());
adapter.setQos(1);
adapter.setOutputChannel(messageInputChannel);
return adapter;
}
}
接受消息
@Configuration
public class MessageReceiverHandler {
/**
* 收到设备发送来的上行数据的时候执行,具体怎么做取决于业务,比如这里面可能是设备发来的一些传感器数据,我们需要保存并发送到统计平台
* @return
*/
@Bean
@ServiceActivator(inputChannel = "messageInputChannel")
public MessageHandler messageHandler(){
return message -> {
//获取到消息正文
Object payload = message.getPayload();
System.err.println(payload);
//处理消息
System.err.println("等下就处理消息");
};
}
}
发送消息/接受消息都要配置处理器