最近在学习物联网相关的通信协议,重点研究了MQTT。从原理理解到实际项目集成,发现它和之前学的Kafka等传统消息队列差异很大,尤其适配资源受限的物联网设备场景。这篇总结就从核心原理入手,再结合智能家居设备状态监控的业务场景,完整梳理Spring Boot集成MQTT的实战过程,帮助自己加深理解
一、MQTT核心原理:轻量级物联网通信的本质
刚开始接触MQTT时,觉得"消息队列遥测传输"这个名字很复杂,深入学习后发现它的核心逻辑其实很简单------就是为带宽有限、算力不强的物联网设备设计的轻量级通信协议。像家里的温湿度传感器、Wi-Fi灯泡、智能插座这些设备,不需要复杂的通信逻辑,只需要快速、可靠地交换少量数据,MQTT正好适配这种需求。
1. 核心设计思想:发布/订阅(Pub/Sub)模型
MQTT最核心的思想就是发布/订阅模型,和我们日常用的微信群很像,理解起来很直观:
- 发布者(Publisher):相当于在群里发消息的人,对应物联网场景中的设备(比如温湿度传感器、手机APP),负责将消息发送到指定主题。
- 订阅者(Subscriber):相当于群里的成员,对应需要接收消息的设备(比如智能灯泡、监控面板),通过订阅主题获取相关消息。
- 消息(Message):设备之间传递的具体内容,比如传感器采集的"温度25.5°C"、控制灯泡的"on/off"指令。因为MQTT主打轻量,消息内容要尽量简洁,节省带宽。
举个智能家居的实际例子:家里的ESP32智能灯泡订阅了"home/livingroom/light"主题,手机APP作为发布者向这个主题发送"on"消息,灯泡收到消息后就会点亮;反过来,灯泡也会向该主题发布自己的开关状态,手机APP订阅后就能实时同步状态。
2. 关键核心概念
要真正用好MQTT,这几个核心概念必须理清,是后续实战配置的基础:
(1)主题(Topic):消息的"地址"
主题是MQTT的灵魂,相当于消息的接收地址,用字符串表示,通过斜杠"/"划分层级,结构清晰。比如:
- home/kitchen/temperature:厨房温湿度传感器的消息主题
- home/livingroom/light:客厅智能灯泡的控制主题
- home/bedroom/curtain:卧室窗帘的控制主题
需要注意的是,主题区分大小写,"Home/Light"和"home/light"是两个完全不同的主题,配置时如果写错,消息就会发送失败,这是学习过程中很容易踩的坑。
(2)代理(Broker):消息的"中转站"
MQTT不像传统消息队列那样可以直接点对点通信,必须通过一个核心枢纽------代理(Broker)来中转消息。Broker的作用就是接收所有发布者的消息,然后根据订阅关系,将消息分发给对应的订阅者。
对于开发者来说,最常用的Broker是Mosquitto,开源免费且部署简单。入门阶段可以用公共的MQTT服务器(比如HiveMQ的公共服务器),不用自己部署;如果是实际项目,把Mosquitto部署在树莓派或云服务器上,就能实现设备的跨网络通信。
(3)服务质量(QoS):消息可靠性的"保障等级"
MQTT为不同场景设计了3个级别的服务质量,用来平衡消息可靠性和传输效率,实战中需要根据业务需求选择:
- QoS 0(最多一次):消息只发送一次,不保证送达,也不重发。适合对可靠性要求低的场景,比如传感器周期性上报的非关键数据(丢一条影响不大)。
- QoS 1(至少一次):保证消息至少送达一次,如果没收到确认会重发。适合设备控制指令,比如点亮灯泡的指令必须送达。
- QoS 2(仅一次):保证消息仅送达一次,不重复。通过四次握手实现,可靠性最高,但传输开销也最大,适合金融类物联网设备的交易消息。
3. MQTT与传统消息队列的核心差异
学习过程中经常会把MQTT和之前学的Kafka、RabbitMQ对比,总结了几个关键差异,帮助理解MQTT的适用场景:
- 设计目标:MQTT主打轻量、低带宽、适配资源受限设备;传统消息队列追求高吞吐、强持久化,适合后端服务间的大数据量通信。
- 通信模式:MQTT是广播式,一条消息可被所有订阅主题的设备接收;传统消息队列多是独占式,一条消息仅被消费组内一个消费者处理。
- 资源占用:MQTT协议头仅2字节,资源占用极低;传统消息队列协议头复杂,对设备算力和带宽要求更高。
二、实战:Spring Boot集成MQTT(智能家居设备监控场景)
下面结合"智能家居设备状态监控"的业务场景展开实战:需求是实现温湿度传感器数据上报、智能灯泡状态控制与同步,用Spring Boot搭建后端服务,负责接收传感器数据并展示,同时支持通过接口控制智能设备。
1. 业务场景时序图
先通过时序图理清整个业务流程,直观理解各组件的交互逻辑:

2. 环境准备
实战前先确认开发环境和依赖,避免后续出现兼容问题:
- 开发环境:JDK 1.8及以上、Maven 3.x、Spring Boot 2.x及以上
- MQTT服务器:这里用HiveMQ公共服务器(tcp://broker.hivemq.com:1883),无需本地部署,方便测试
- 核心依赖:Spring Web(用于提供测试接口)、Spring Integration MQTT(MQTT集成核心依赖)
3. 项目搭建与配置
(1)创建Spring Boot项目并添加依赖
用Spring Initializr(start.spring.io/)快速创建项目,在pom.xml中添加以下依赖:
<dependencies>
<!-- Spring Web依赖,用于提供测试接口 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Integration MQTT核心依赖 -->
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
</dependency>
</dependencies>
(2)配置MQTT连接信息
在application.properties中添加MQTT相关配置,方便后续维护和修改:
# MQTT服务器地址
spring.mqtt.url=tcp://broker.hivemq.com:1883
# 客户端唯一标识(注意:同一Broker下client-id不能重复)
spring.mqtt.client-id=springboot-mqtt-demo
# 默认订阅主题(多个主题用逗号分隔)
spring.mqtt.default-topic=home/kitchen/temperature,home/livingroom/light
# MQTT服务器用户名(公共服务器无需认证,留空即可)
spring.mqtt.username=
# MQTT服务器密码
spring.mqtt.password=
(3)创建MQTT核心配置类
创建MqttConfig配置类,负责配置MQTT客户端工厂、消息通道、消息接收和发送适配器。学习过程中发现,这个类是集成的核心,需要理解每个组件的作用:
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
@Configuration
public class MqttConfig {
/**
* 创建MQTT客户端工厂,配置连接选项
*/
@Bean
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
MqttConnectOptions options = new MqttConnectOptions();
// 设置MQTT服务器地址
options.setServerURIs(new String[]{ "${spring.mqtt.url}" });
// 设置用户名密码(公共服务器无需配置)
options.setUserName("${spring.mqtt.username}");
options.setPassword("${spring.mqtt.password}".toCharArray());
// 配置断开重连(确保连接稳定性)
options.setAutomaticReconnect(true);
factory.setConnectionOptions(options);
return factory;
}
/**
* 消息输入通道:用于接收MQTT消息
*/
@Bean
public MessageChannel mqttInputChannel() {
return new DirectChannel();
}
/**
* 消息驱动通道适配器:订阅主题并接收消息
*/
@Bean
public MqttPahoMessageDrivenChannelAdapter inbound() {
// 参数:client-id、客户端工厂、订阅的主题
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter("${spring.mqtt.client-id}",
mqttClientFactory(), "${spring.mqtt.default-topic}");
// 配置超时时间
adapter.setCompletionTimeout(5000);
// 配置消息转换器
adapter.setConverter(new DefaultPahoMessageConverter());
// 设置服务质量(QoS 1,保证至少送达一次)
adapter.setQos(1);
// 绑定消息输入通道
adapter.setOutputChannel(mqttInputChannel());
return adapter;
}
/**
* 消息处理器:处理接收到的MQTT消息(核心业务逻辑)
*/
@Bean
@ServiceActivator(inputChannel = "mqttInputChannel")
public MessageHandler handler() {
return message -> {
// 获取消息主题
String topic = message.getHeaders().get("mqtt_receivedTopic").toString();
// 获取消息内容
String payload = message.getPayload().toString();
System.out.println("接收到主题[" + topic + "]的消息:" + payload);
// 业务逻辑:根据主题区分处理(存储传感器数据/更新设备状态)
if ("home/kitchen/temperature".equals(topic)) {
// 模拟存储温湿度数据到数据库
System.out.println("温湿度数据已存储:" + payload);
} else if ("home/livingroom/light".equals(topic)) {
// 模拟更新灯泡状态到缓存
System.out.println("灯泡状态已更新:" + payload);
}
};
}
/**
* 消息输出通道:用于发送MQTT消息
*/
@Bean
public MessageChannel mqttOutputChannel() {
return new DirectChannel();
}
/**
* 消息发送处理器:发布消息到MQTT主题
*/
@Bean
@ServiceActivator(inputChannel = "mqttOutputChannel")
public MessageHandler mqttOutbound() {
// 参数:client-id(发布者客户端ID,需与订阅者区分)、客户端工厂
MqttPahoMessageHandler messageHandler =
new MqttPahoMessageHandler("${spring.mqtt.client-id}-publisher", mqttClientFactory());
// 设置异步发送
messageHandler.setAsync(true);
// 设置默认发布主题
messageHandler.setDefaultTopic("${spring.mqtt.default-topic}");
return messageHandler;
}
}
4. 实现消息发送服务
创建MqttMessageSender服务类,封装消息发送逻辑,供控制器调用。这部分比较简单,核心是通过消息输出通道发送消息:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Service;
@Service
public class MqttMessageSender {
// 注入消息输出通道
@Autowired
private MessageChannel mqttOutputChannel;
/**
* 发送消息到默认主题
* @param message 消息内容
*/
public void sendMessage(String message) {
mqttOutputChannel.send(new GenericMessage<>(message));
}
/**
* 发送消息到指定主题(灵活适配不同设备)
* @param topic 目标主题
* @param message 消息内容
*/
public void sendMessageToTopic(String topic, String message) {
// 携带主题信息发送消息
mqttOutputChannel.send(new GenericMessage<>(message,
org.springframework.util.CollectionUtils.newHashMap("mqtt_topic", topic)));
}
}
5. 创建测试控制器
创建MqttController,提供HTTP接口测试消息发送功能,模拟手机APP发送控制指令的场景:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MqttController {
@Autowired
private MqttMessageSender mqttMessageSender;
/**
* 发送消息到默认主题
* 测试地址:http://localhost:8080/send?message=HelloMQTT
*/
@GetMapping("/send")
public String sendMessage(@RequestParam String message) {
mqttMessageSender.sendMessage(message);
return "消息已发送到默认主题:" + message;
}
/**
* 发送控制指令到指定设备主题
* 测试地址:http://localhost:8080/sendToDevice?topic=home/livingroom/light&message=on
*/
@GetMapping("/sendToDevice")
public String sendToDevice(@RequestParam String topic, @RequestParam String message) {
mqttMessageSender.sendMessageToTopic(topic, message);
return "消息已发送到主题[" + topic + "]:" + message;
}
}
6. 测试验证
项目搭建完成后,启动Spring Boot应用,进行以下测试,验证整个流程是否正常:
- 测试1:发送控制指令控制灯泡。访问http://localhost:8080/sendToDevice?topic=home/livingroom/light\&message=on,查看控制台输出,应该能看到"接收到主题\[home/livingroom/light\]的消息:on",同时打印"灯泡状态已更新:on"。
- 测试2:模拟传感器上报数据。可以用MQTTX(一款MQTT测试工具)作为发布者,向主题home/kitchen/temperature发送消息"温度:25.5°C,湿度:60%",查看Spring Boot控制台,应该能接收到该消息并打印"温湿度数据已存储:温度:25.5°C,湿度:60%"。
三、学习总结与扩展思考
- MQTT的核心是发布/订阅模型,依赖Broker中转消息,适合轻量级物联网设备通信。
- Spring Boot集成MQTT的关键是配置客户端工厂、消息通道和适配器,业务逻辑集中在消息处理器中。
- 实战中要根据业务场景选择合适的QoS等级,平衡可靠性和传输效率。
后续可以扩展的方向:一是搭建本地Mosquitto服务器,实现设备的局域网通信;二是增加消息持久化功能,避免服务重启后数据丢失;三是集成前端页面,实现设备状态的可视化监控。这些扩展能让项目更贴近实际应用场景,进一步加深对MQTT的理解。
|---------|--------------------------------------|-------------------------------------|
| 对比维度 | MQTT | 传统消息队列(Kafka/RabbitMQ) |
| 消息分发模式 | 广播式:一条消息可被所有订阅该主题的客户端接收(多订阅者共享同一条消息) | 独占式:一条消息仅被消费组内一个消费者处理(负载均衡到单个实例) |
| 主题/队列创建 | 无需预创建主题,发布时自动生成,灵活适配动态设备接入 | 需显式创建队列/主题,权限与配置更严格,适合固定服务架构 |
| 未订阅消息处理 | 无订阅者时,消息直接丢弃(默认不持久化无订阅消息) | 消息持久化存储,直到被消费(或过期),避免数据丢失 |
| 会话与连接 | 支持长连接+持久会话,断连后可恢复未接收消息,适配设备频繁离线场景 | 多为短连接+消费位移管理,依赖Broker存储位移,适合服务端稳定通信 |
四、参考链接
https://zhuanlan.zhihu.com/p/1915210281279287476
https://juejin.cn/post/7477599259834548278