当前位置: 首页 > news >正文

SpringBoot通过Moquette集成MQTT Broker快速实现设备管理消息订阅与发布

一、引言

1.1 什么是 MQTT

MQTT 是一种基于发布/订阅范式的轻量级消息传输协议。该协议专为计算资源受限、带宽有限以及网络连接不稳定的设备设计,具有报文结构简洁、功耗低、传输可靠等特点。

1.2 为什么选择 Moquette

当业务需求比较简单没必要或开发受限无法在服务器部署第三方服务(例如 EMQX、Mosquitto、HiveMQ),那么就需要在 SpringBoot 中集成 MQTT Broker。
Moquette 是一个轻量级嵌入式 Broker,可以轻松封装在其他应用程序中。但由于0.18之前的版本不支持 MQTT5,所以这里选择 Fraunhofer 维护的新分支。

二、依赖配置

<!-- MQTT Broker:Moquette --><dependency><groupId>de.fraunhofer.iosb.io.moquette</groupId><artifactId>moquette-broker</artifactId><version>0.18.4</version></dependency>

三、代码整合

3.1 创建配置类

importio.moquette.broker.Server;importio.moquette.broker.config.IConfig;importio.moquette.broker.config.MemoryConfig;importio.moquette.interception.InterceptHandler;importjakarta.annotation.PostConstruct;importjakarta.annotation.PreDestroy;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjava.util.Arrays;importjava.util.List;importjava.util.Properties;@ConfigurationpublicclassMqttBrokerConfig{privateServermqttBroker;@AutowiredprivateCustomInterceptHandlerinterceptHandler;@PostConstructpublicvoidstartBroker(){mqttBroker=newServer();Propertiesprops=newProperties();props.setProperty("port","1883");//监听端口,默认 1883props.setProperty("host","0.0.0.0");//绑定地址,0.0.0.0 表示允许所有 IP 访问props.setProperty("allow_anonymous","false");//禁止匿名登录,必须用户名+密码认证(必须设为 false)//性能相关配置props.setProperty("max_inflight_queue","1000");//单个客户端最大未确认消息队列长度,防止消息堆积props.setProperty("netty.mqtt.message_size","65536");//最大消息体大小(64KB)props.setProperty("persistence_enabled","true");//是否开启持久化props.setProperty("persistence_type","file");//持久化存储类型props.setProperty("data_path","D:\\haooo\\tempFile\\mqttFile");//持久化文件存放目录props.setProperty("persistence_recover_on_startup","true");//持久化启动时自动恢复props.setProperty("max_queued_messages","10000");//最大队列消息数IConfigconfig=newMemoryConfig(props);try{List<InterceptHandler>handlers=Arrays.asList(interceptHandler);mqttBroker.startServer(config,handlers,null,deviceAuthenticator,null);System.out.println("✅ Moquette MQTT Broker 启动成功,端口 1883");}catch(Exceptione){System.err.println("❌ Moquette Broker 启动失败");e.printStackTrace();}}@PreDestroypublicvoidstopBroker(){if(mqttBroker!=null){mqttBroker.removeInterceptHandler(interceptHandler);mqttBroker.stopServer();System.out.println("✅ Moquette MQTT Broker 已停止");}}@BeanpublicServermqttServer(){returnmqttBroker;//返回已启动的实例}}

3.2 自定义认证

importcom.github.benmanes.caffeine.cache.Cache;importcom.github.benmanes.caffeine.cache.Caffeine;importio.moquette.broker.security.IAuthenticator;importorg.springframework.stereotype.Component;importjava.time.Duration;/** * 自定义认证 * Moquette 支持插件式认证,实现 IAuthenticator */@ComponentpublicclassDeviceAuthenticatorimplementsIAuthenticator{privatefinalCache<String,Boolean>authCache=Caffeine.newBuilder().expireAfterWrite(Duration.ofMinutes(5)).build();@OverridepublicbooleancheckValid(StringclientId,Stringusername,byte[]password){if(username==null||password==null||clientId==null){System.out.println("❌ 设备认证失败:用户名或密码或客户端标识为空");returnfalse;}StringpwdStr=newString(password);//验证设备是否存在(5分钟内的验证结果进行缓存,避免认证失败的设备频繁重试)Booleancached=authCache.getIfPresent(clientId+":"+username);if(cached!=null){returncached;}else{//数据库查询Devicedevice=DeviceMapper.selectDevice(username,clientId,pwdStr);if(device!=null){System.out.println("✅ 设备认证成功: "+clientId+" | "+username+" | "+pwdStr);authCache.put(clientId+":"+username,true);returntrue;}else{authCache.put(clientId+":"+username,false);}}System.out.println("❌ 设备认证失败: "+clientId+" | "+username+" | "+pwdStr);returnfalse;}}

3.3 消息处理

importio.moquette.interception.AbstractInterceptHandler;importio.moquette.interception.messages.InterceptConnectMessage;importio.moquette.interception.messages.InterceptDisconnectMessage;importio.moquette.interception.messages.InterceptPublishMessage;importio.netty.buffer.ByteBuf;importorg.springframework.stereotype.Component;importjava.nio.charset.StandardCharsets;@ComponentpublicclassCustomInterceptHandlerextendsAbstractInterceptHandler{@OverridepublicStringgetID(){return"CustomInterceptHandler";}@OverridepublicvoidonConnect(InterceptConnectMessagemsg){System.out.println("✅ 设备连接成功: "+msg.getClientID()+" | "+msg.getUsername());//TODO: 更新数据库设备在线离线状态}@OverridepublicvoidonDisconnect(InterceptDisconnectMessagemsg){System.out.println("❌ 设备连接断开: "+msg.getClientID()+" | "+msg.getUsername());//TODO: 更新数据库设备在线离线状态}@OverridepublicvoidonPublish(InterceptPublishMessagemsg){ByteBufbuf=msg.getPayload();if(buf==null||buf.readableBytes()==0){return;}byte[]bytes=newbyte[buf.readableBytes()];buf.getBytes(buf.readerIndex(),bytes);Stringpayload=newString(bytes,StandardCharsets.UTF_8);System.out.println("✅ 设备发布消息: "+msg.getClientID()+" | "+msg.getUsername()+" | "+msg.getTopicName()+" | "+payload);//处理设备发送的消息if(msg.getTopicName().matches("devices/[^/]+/[^/]+/resp")){//处理设备发布主题}else{System.out.println("❌ 未匹配设备发布消息: "+msg.getClientID()+" | "+msg.getUsername()+" | "+msg.getTopicName()+" | "+payload);}}@OverridepublicvoidonSessionLoopError(Throwablecause){System.out.println("❌ 会话循环错误: "+cause.getMessage());}}

3.4 发送指令

importcom.alibaba.fastjson2.JSONObject;importio.moquette.broker.Server;importio.netty.buffer.ByteBuf;importio.netty.buffer.Unpooled;importio.netty.handler.codec.mqtt.*;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Lazy;importorg.springframework.stereotype.Service;@ServicepublicclassMqttCommandService{@Autowired@LazyprivateServermqttBroker;/** * 向指定设备发送指令 * @param clientId 客户端标识 * @param username 设备序列号 * @param command 指令内容 */publicvoidsendCommand(StringclientId,Stringusername,JSONObjectcommand){Stringtopic="devices/"+clientId+"/"+username+"/cmd";//订阅主题try{MqttFixedHeadermqttFixedHeader=newMqttFixedHeader(MqttMessageType.PUBLISH,false,MqttQoS.AT_LEAST_ONCE,false,0);MqttPublishVariableHeadervariableHeader=newMqttPublishVariableHeader(topic,0);ByteBufpayload=Unpooled.wrappedBuffer(command.toJSONString().getBytes());MqttPublishMessagemsg=newMqttPublishMessage(mqttFixedHeader,variableHeader,payload);mqttBroker.internalPublish(msg,clientId);System.out.println("✅ 已下发指令到设备 "+username+" → "+command);}catch(Exceptione){e.printStackTrace();}}}

四、测试

使用 MQTTX 进行测试

/** * 测试Controller */@RestController@RequestMapping("/test")publicclassTestControllerextendsBaseController{@AutowiredprivateMqttCommandServicemqttCommandService;/** * MQTT消息发送 */@PostMapping("/command")publicRcommand(@RequestBodyJSONObjectparams){mqttCommandService.sendCommand(params.getString("clientId"),params.getString("username"),params.getJSONObject("command"));returnsuccess();}}



http://www.jsqmd.com/news/1204883/

相关文章:

  • C# 多线程进阶知识点(线程优先级、多委托传参、线程锁、死锁)
  • Liunx内核IO多路复用selector机制简介
  • 梁山派学习笔记1
  • 宝珀官方售后服务中心电话和完整地址实地考察报告_多信源验证(2026年7月更新) - 宝珀官方售后服务中心
  • 文件夹加密实战指南:从AES原理到EFS、BitLocker与VeraCrypt方案选择
  • 从冰冷的对话框到“会说话的脸“:3D数字人交互技术的落地实践
  • VMware虚拟机磁盘扩容与LVM分区管理实战
  • Ubuntu 14.04 LTS 系统安装与优化指南
  • Python新手入门:从Hello World到调试技巧
  • LangServe革新LLM应用部署:从LangChain链到生产级API的一站式方案
  • 2026 年现阶段,鄯善热门的中央空调安装定做厂家哪个好,安装不当?省钱的秘密藏在这套系统里 - 企业推荐管【认证】
  • Windows服务化运行与shawl工具实战指南
  • 大模型自动评分系统:三种方法、四大缺陷与实战应对策略
  • HarmonyOS7 图标按钮行:用 Row 把图文按钮做得更顺手
  • Linux与Windows虚拟机环境搭建与优化指南
  • 普通烤火罩容易起火,新 e 选安全吗?
  • Selenium框架内网页源码获取:iframe与SPA动态内容实战指南
  • HarmonyOS7 加载状态展示页实战:LoadingProgress 不只是会用,还要用得顺手
  • C++异常处理:从基础语法到RAII与noexcept实战指南
  • 从数组逆向搜索到文件加密:Java实战编程与数据安全入门
  • Claude企业版协作功能解析:项目可见性与团队AI知识库管理
  • python3GUI--使用PyQt5仿飞车登录界面(详细图文介绍)
  • Waveloom:本地化AI编程助手,终端环境下的代码智能辅助方案
  • macOS系统降级完整指南与常见问题解决
  • 谷歌云AI工具Kubeflow Pipelines与AI Hub深度解析
  • FPGA阵列相机系统设计与实时4K视频处理
  • MacBook外接显示器时如何有效关闭内屏
  • 云服务官方域名滥用钓鱼攻击深度解析与防御实战
  • Kali Linux基础设置与优化全攻略
  • 量化交易系统构建指南:从策略开发到实盘部署的技术实践