【EMQX】 Spring Cloud 集成MQTT并异步入库(mongodb)
创始人
2024-02-05 19:33:48
0

1. 需求:

        1. Spring boot项目,集成mqtt,订阅物联网设备数据上报topic1

        2. 处理topic1,并异步入库到mongodb

        3. mongodb 根据设备id分集合

2. 代码实现

2.1 引入pom

  • 子项目(本项目)
        org.eclipse.pahoorg.eclipse.paho.client.mqttv31.2.2org.springframework.bootspring-boot-starter-data-mongodbcom.spring4allmongodb-plus-spring-boot-starter
  • 父-引入pom
    org.springframework.bootspring-boot-starter-parent2.3.5.RELEASEorg.springframework.cloudspring-cloud-dependenciesHoxton.SR12pomimport

2.2 启动类引入注解 @EnableMongoPlus

2.3  application.yml mongodb配置

spring:profiles:active: proddata:mongodb:uri: mongodb://root:${mongo-password}@${host}:${mongodbport}/hzdoption:# 最小连接数min-connection-per-host: 10# 最大连接数max-connection-per-host: 200#允许阻塞连接乘数的线程threads-allowed-to-block-for-connection-multiplier: 5#服务器超时server-selection-timeout: 30000#最长等待时间max-wait-time: 120000#最大连接空闲时间max-connection-idle-time: 0#最大连接生存时间max-connection-life-time: 0#连接超时connect-timeout: 10000#套接字超时10000socket-timeout: 0socket-keep-alive: falsessl-enabled: falsessl-invalid-host-name-allowed: falsealways-use-m-beans: falseheartbeat-socket-timeout: 20000heartbeat-connect-timeout: 20000min-heartbeat-frequency: 500heartbeat-frequency: 10000local-threshold: 15

2.4 代码

1. EmqClient.java                  连接emqx

  • init()  使用@PostConstruct注解,开机自动连接EMQX + 订阅
  • reConnect()  断线重连后,订阅主题
package com.hzd.mqtt.client;import com.hzd.mqtt.enums.QosEnum;
import com.hzd.mqtt.properties.MqttProperties;
import lombok.extern.slf4j.Slf4j;
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;import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;/*** Created by*/
@Component
@Slf4j
public class EmqClient {private IMqttClient client;@Autowiredprivate MqttProperties mqttProperties;@Autowiredprivate MqttCallback mqttCallback;@Autowiredprivate MqttProperties properties;private static final String topic_hzd = "/hzd/pub/#";private static final String topic_device_connect = "/device/connect/#";@PostConstructpublic void init() {MqttClientPersistence mempersitence = new MemoryPersistence();try {if (client == null) {client = new MqttClient(mqttProperties.getBrokerUrl(), mqttProperties.getClientId(), mempersitence);}} catch (MqttException e) {log.error("初始化客户端mqttClient对象失败,errormsg={},brokerUrl={},clientId={}", e.getMessage(), mqttProperties.getBrokerUrl(), mqttProperties.getClientId());}//连接服务端connect(properties.getUsername(), properties.getPassword());//订阅一个主题subscribe(topic_hzd, QosEnum.QoS2);subscribe(topic_device_connect, QosEnum.QoS2);log.info("EmqClient连接成功,订阅成功.topic={},{}", topic_hzd, topic_device_connect);}/*** 连接broker** @param username* @param password*/public void connect(String username, String password) {MqttConnectOptions options = new MqttConnectOptions();options.setAutomaticReconnect(true);options.setUserName(username);options.setPassword(password.toCharArray());options.setCleanSession(true);client.setCallback(mqttCallback);try {client.connect(options);} catch (MqttException e) {log.error("mqtt客户端连接服务端失败,失败原因{}", e.getMessage());}}/*** 断开连接*/@PreDestroypublic void disConnect() {try {client.disconnect();} catch (MqttException e) {log.error("断开连接产生异常,异常信息{}", e.getMessage());}}/*** 重连*/public void reConnect() {try {client.reconnect();while (true){if (client.isConnected()){subscribe(topic_hzd, QosEnum.QoS2);subscribe(topic_device_connect, QosEnum.QoS2);break;}else{Thread.sleep(100);log.info("重连中,请稍后......");}}log.info("EmqClient连接成功,订阅成功.topic={},{}", topic_hzd, topic_device_connect);} catch (MqttException | InterruptedException e) {log.error("重连失败,失败原因{}", e.getMessage());}}/*** 发布消息** @param topic* @param msg* @param qos* @param retain*/public void publish(String topic, String msg, QosEnum qos, boolean retain) {MqttMessage mqttMessage = new MqttMessage();mqttMessage.setPayload(msg.getBytes());mqttMessage.setQos(qos.value());mqttMessage.setRetained(retain);try {client.publish(topic, mqttMessage);} catch (MqttException e) {log.error("发布消息失败,errormsg={},topic={},msg={},qos={},retain={}", e.getMessage(), topic, msg, qos.value(), retain);}}/*** 订阅** @param topicFilter* @param qos*/public void subscribe(String topicFilter, QosEnum qos) {try {client.subscribe(topicFilter, qos.value());} catch (MqttException e) {log.error("订阅主题失败,errormsg={},topicFilter={},qos={}", e.getMessage(), topicFilter, qos.value());}}/*** 取消订阅** @param topicFilter*/public void unSubscribe(String topicFilter) {try {client.unsubscribe(topicFilter);} catch (MqttException e) {log.error("取消订阅失败,errormsg={},topicfiler={}", e.getMessage(), topicFilter);}}}

2. MessageCallback.java         处理订阅消息

  • init() 加载异步入库处理类DataStorage
  • connectionLost(Throwable cause) 断线重连
  • messageArrived(String topic, MqttMessage message)  处理订阅消息
package com.hzd.mqtt.client;import com.hzd.mqtt.async.DataStorage;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;/****/
@Component
@Slf4j
public class MessageCallback implements MqttCallback {@Autowiredprivate DataStorage dataStorage;@Autowiredprivate EmqClient emqClient;public static MessageCallback MessageCallback;@PostConstruct //通过@PostConstruct实现初始化bean之前进行的操作public void init() {log.info("初始化");MessageCallback = this;
//        MessageCallback.dataPageRepository = this.dataPageRepository;MessageCallback.dataStorage = this.dataStorage;}/*** 丢失了对服务端的连接后触发的回调** @param cause*/@Overridepublic void connectionLost(Throwable cause) {// 资源的清理  重连log.info("断开emqx连接,开始重连......");emqClient.reConnect();log.info("emqx连接成功......");}/*** 应用收到消息后触发的回调** @param topic* @param message* @throws Exception*/@Overridepublic void messageArrived(String topic, MqttMessage message) throws Exception {dataStorage.saveDataToMongo(topic, message);}/*** 消息发布者消息发布完成产生的回调** @param token*/@Overridepublic void deliveryComplete(IMqttDeliveryToken token) {int messageId = token.getMessageId();String[] topics = token.getTopics();log.info("消息发布完成,messageid={},topics={}", messageId, topics);}
}


3. DataStorage.java          异步入库处理类

package com.hzd.mqtt.async;import com.alibaba.fastjson2.JSON;
import com.hzd.entity.po.ControlLog;
import com.hzd.entity.po.DataExtend;
import com.hzd.entity.po.MqttClientLog;
import com.hzd.entity.vo.MqttPayload;
import com.hzd.entity.vo.MqttTopic;
import com.hzd.mqtt.enums.TopicType;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.data.mongodb.UncategorizedMongoDbException;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.Date;/*** Coding by 李炯 on 2022/10/28 15:12*/
@Component
@Slf4j
public class DataStorage {@Resourceprivate MongoTemplate mongoTemplate;//    @Resource
//    private DataPageRepository dataPageRepository;@Asyncpublic void saveDataToMongo(String topic, MqttMessage message) {MqttTopic t = MqttTopic.init(topic);String payload = new String(message.getPayload());if (t == null) return;if (payload.contains("\"type\":\"data\"")) {DataExtend dataExtend = DataExtend.init(payload);mongoTemplate.insert(dataExtend, t.getFac_id());log.info("设备数据上报,topic={},fac_id={},messageid={},qos={},payload={}",topic, t.getFac_id(), message.getId(), message.getQos(), payload);} else if (payload.contains("\"type\":\"config\"")) {ControlLog controlLog = ControlLog.init(payload);Query query = new Query(Criteria.where("_id").is(controlLog.get_id()));Update update = Update.update("status", controlLog.getStatus());update.set("status", controlLog.getStatus());update.set("msg", controlLog.getMsg());if (null != controlLog.getRelayIndexLst()) {update.set("relayIndexLst", controlLog.getRelayIndexLst());}if (null != controlLog.getRelayNumLst()) {update.set("relayNumLst", controlLog.getRelayNumLst());}if (null != controlLog.getReadInterval()) {update.set("readInterval", controlLog.getReadInterval());}if (null != controlLog.getRelayphoto()) {update.set("relayphoto", controlLog.getRelayphoto());}if (null != controlLog.getLongitude()) {update.set("longitude", controlLog.getLongitude());}if (null != controlLog.getLatitude()) {update.set("latitude", controlLog.getLatitude());}if (null != controlLog.getControlStatus()) {update.set("controlStatus", controlLog.getControlStatus());}if (null != controlLog.getReserve()) {update.set("reserve", controlLog.getReserve());}update.set("updateTime", new Date());mongoTemplate.upsert(query, update, "log_control");log.info("{},topic={},messageid={},qos={},payload={}","配置返回", topic, message.getId(), message.getQos(), payload);} else if (t.getTopicType() == TopicType.connect) {MqttPayload pl = MqttPayload.init(payload);MqttClientLog clientLog = MqttClientLog.init(pl);updateConnectStatus(topic, clientLog);log.info("{},topic={},messageid={},qos={},payload={}",clientLog.getStatus(), topic, message.getId(), message.getQos(), payload);}}@Asyncpublic void updateConnectStatus(String topic, MqttClientLog clientLog) {try{MqttTopic t = MqttTopic.init(topic);if (t == null) return;mongoTemplate.insert(clientLog, "log_mqtt_client");Query query = new Query(Criteria.where("username").is(clientLog.getUserName()).and("deviceid").is(clientLog.getDeviceId()));Update update = Update.update("connectStatus", clientLog.getConnectStatus());update.set("status", clientLog.getStatus());update.set("updateTime", clientLog.getUpdateTime());mongoTemplate.upsert(query, update, "status_mqtt_client");}catch (Exception e){log.error("mongoDB 错误:MqttClientLog={},错误消息为{}",JSON.toJSONString(clientLog),e.getMessage());}}
}

相关内容

热门资讯

你的晚安是我的早安是什么歌曲 你的晚安是我的早安是什么歌曲苏天伦《你的早安是我的晚安》“你的晚安是我的早安”是单小源的歌曲《东京遇...
积极进取的小故事 积极进取的小故事现代的普通人,不要名人的。不能与别人重复,尽快回答   啄木鸟的故事       啄...
熊出没之探险日记3什么时候播出... 熊出没之探险日记3什么时候播出?熊出没之探险日记3,春节前播放。熊出没只是探险日记三2020年5月4...
谁知道所有有关“七”的歌?拜托... 谁知道所有有关“七”的歌?拜托了各位 谢谢就是歌曲名里有“七”这个字的!谢谢七月七迅谈日晴 看我七十...
求一本小说 女主穿越了三次 每... 求一本小说 女主穿越了三次 每次都在福临身边 后来怀孕了孩子被打掉了那个 女主叫什么雯?那个女主就是...
如果记忆不说话,流年也会开出花... 如果记忆不说话,流年也会开出花的基本信息书 名:《如果记忆不弯饥好说话,流年也会开出花》埋铅 作 者...
你好,旧时光漫画版在哪里可以看... 你好,旧时光漫画版在哪里可以看?暂时在绘心上连载
一首英文歌,男的组合唱的,MV... 一首英文歌,男的组合唱的,MV是一个婚礼的过程。求歌名。是不是darin的can'tstoplove...
为什么很多人喜欢用胶片相机? 为什么很多人喜欢用胶片相机?有一种情怀叫做“怀旧“吧,现在数码相机越来越普遍了,已经到了”全民摄影“...
女主先爱上男主,男主却不喜欢女... 女主先爱上男主,男主却不喜欢女主或者是另有所爱,最后女主男主还是在一起的穿越小说。有木有再生缘:我的...
爱情失恋伤感句子 爱情失恋伤感句子越是美好的从前,越幸福的曾经,现在只能带来锥心的疼痛,痛到撕心裂肺,肝肠寸断,终于痛...
24岁穿这个会不会显老 24岁穿这个会不会显老有点显老,这个颜色款式,颜色有点暗,没有活力,属于那种气质佳,长得高雅的女人,...
哈尔的移动城堡英语版 哈尔的移动城堡英语版可以发给我吗度盘~请查收~
秦时明月之万里长城什么时候播 秦时明月之万里长城什么时候播据说是今年暑假开播别急,官网什么的信他你就输了,12年之前底应该会出,杭...
孩子会得抽动症吗? 孩子会得抽动症吗?我天生的气性比较大,有时跟别人斗嘴时候就会手脚哆嗦,麻木,我问一下这是不是抽动症就...
亨德尔一生为音乐献出了怎样的贡... 亨德尔一生为音乐献出了怎样的贡献?亨德尔一生写了歌剧41部,清唱剧21部,以及大量的管乐器与弦乐器的...
礼仪起源和发展的经典故事? 礼仪起源和发展的经典故事?一、礼仪的起源;1、天神生礼仪;2、礼为天地人的统一体;3、礼产生于人的自...
描写桂林山水的句子有哪些? 描写桂林山水的句子有哪些?天下风光数桂林有杨万里的“梅花五岭八桂林,青罗带绕碧玉簪”;有邹应龙的“无...
避免与强敌正面对决的成语 避免与强敌正面对决的成语避免与强敌正面对决的成语避实就虚 【近义】避重就轻、避难就易、声东击西【反义...
多愁善感类的成语 多愁善感类的成语心细如发【解释】:极言小心谨慎,考虑周密。亦作“心细于发”。【出自】:吴梅《题天香石...