1. 首先是docker安装emqx
docker pull emqx:latest
docker run --restart=always -d --name emqx -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 8883:8883 -p 18083:18083 emqx:latest
# 如果需要使用网页控制台的websocket记得加上8083端口号。
2.然后就是springboo整合emqx。
基于这个链接
然后大概把代码都看懂就行。主要使用的是其中的com.silky.server.utils.MqttUtil,com.silky.server.config.MessageCallback。里面可以订阅和发布消息。看你们组怎么安排前后端的任务。可能订阅和发布全是后端负责, 所以就要都会使用。
接下来讲讲连接的具体步骤。
2.1 首先是引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- mqtt的相关依赖 -->
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-stream</artifactId>
<version>5.5.13</version>
</dependency>
<!-- 监控执行sql语句的包 -->
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>${p6spy.version}</version>
</dependency>
<!-- 持久层框架mybatis-plus,在idea里面下个插件名字就叫做MybatisPlus,可以生成三层代码 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- 接口调试框架knife4j,具体怎么使用,自行搜索 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<!-- hutools依赖,静态方法包,具体怎么使用,自行搜索 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
</dependencies>
2.2然后是application.yml中配置配置mqtt
mqtt:
open: true
#MQTT-服务器连接地址,如果有多个,用逗号隔开
host: tcp://192.168.228.100:1883
#MQTT-连接服务器默认客户端ID
clientId: mqtt_id
#MQTT-用户名
username: admin
#MQTT-密码
password: admin
#MQTT的订阅主题,发布的时候可以自己自定义主题
topic:
- save
- send
#连接超时
timeout: 2000
#设置会话心跳时间
keepalive: 100
2.3 然后是配置MqttConfig
package com.silky.server.config;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.util.List;
//MQTT订阅
@Slf4j
@Configuration
@ConfigurationProperties("mqtt")
@Data
@Component
public class MqttConfig {
String host;
String clientId;
List<String> topic;
String username;
String password;
Integer timeout;
Integer keepalive;
@Autowired
MessageCallback messageCallback;
@Bean
@ConditionalOnProperty(prefix = "mqtt",name="open",havingValue = "true")
public MqttConnectOptions mqttConnectOptions() {
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(username);
options.setPassword(password.toCharArray());
options.setCleanSession(true);
options.setConnectionTimeout(timeout);
options.setKeepAliveInterval(keepalive);
return options;
}
//基于这个bean做连接就行
@Bean("mqttClient")
@ConditionalOnProperty(prefix = "mqtt",name="open",havingValue = "true")
public MqttClient mqttClient(MqttConnectOptions mqttConnectOptions) {
try {
MqttClient client = new MqttClient(host, clientId);
/*设置回调函数,和js的概念差不多,就是如果接受到消息就调用回调函数*/
client.setCallback(messageCallback);
IMqttToken iMqttToken = client.connectWithResult(mqttConnectOptions);
boolean complete = iMqttToken.isComplete();
log.info("mqtt建立连接:{}", complete);
// 订阅主题
topic.stream().forEach(topic -> {
try {
client.subscribe(topic, 0);
log.info("已订阅topic:{}", topic);
} catch (Exception e) {
e.printStackTrace();
}
});
return client;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("mqtt 连接异常");
}
}
}
2.4然后写MessageCallback这个就是前面的回调函数类
package com.silky.server.config;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.silky.server.domain.po.Diff;
import com.silky.server.service.IDiffService;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* consumer 消费者,接受消息
*/
@Component
@Slf4j
public class MessageCallback implements MqttCallback {
private final MqttClient client;
private final IDiffService diffService;
public MessageCallback(@Lazy MqttClient client, @Lazy IDiffService diffService){
this.client = client;
this.diffService = diffService;
}
@Override
public void connectionLost(Throwable throwable) {
if (client == null || !client.isConnected()) {
log.info("连接断开,正在重连....");
try {
client.reconnect();
if(client.isConnected()){
log.info("mqtt重连完成.");
}
} catch (MqttException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
log.info("接收消息主题 : " + topic);
log.info("接收消息内容 : " + new String(message.getPayload()));
if (topic.equals("save")) {
save(new String(message.getPayload()));
}
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
log.info("deliveryComplete---------" + token.isComplete());
}
//{ "status":"ok","data":[{"imei":"shdhello","humi":"35","temp":"58"}] }
private void save(String message) {
JSONObject jsonObject = JSONUtil.parseObj(message);
Diff diff = Diff.builder()
.imei(jsonObject.getByPath("data[0].imei").toString())
.humi(jsonObject.getByPath("data[0].temp").toString())
.temp(jsonObject.getByPath("data[0].temp").toString())
.time(LocalDateTime.now())
.build();
diffService.saveOne(diff);
}
}
然后我们惊奇的发现这个类无法自动注入,产生了循环依赖,具体解决方案,zhuanlan.zhihu.com/p/638625895 看这篇文章。
我们采用的是延迟加载策略,对MessageCallback采用延迟加载策略。通过构造函数注入。
public MessageCallback(@Lazy MqttClient client, @Lazy IDiffService diffService){
this.client = client;
this.diffService = diffService;
}
2.5 然后保存订阅save的消息
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
log.info("接收消息主题 : " + topic);
log.info("接收消息内容 : " + new String(message.getPayload()));
if (topic.equals("save")) {
save(new String(message.getPayload()));
}
}
//{ "status":"ok","data":[{"imei":"shdhello","humi":"35","temp":"58"}] }
private void save(String message) {
JSONObject jsonObject = JSONUtil.parseObj(message);
Diff diff = Diff.builder()
.imei(jsonObject.getByPath("data[0].imei").toString())
.humi(jsonObject.getByPath("data[0].temp").toString())
.temp(jsonObject.getByPath("data[0].temp").toString())
.time(LocalDateTime.now())
.build();
diffService.saveOne(diff);
}
2.6 controller处理具体的逻辑
这个就看你们小组的了,我写了一个向订阅send发送消息的方法。在DiffController里面。
3.后续处理
我会把代码放在github上,如果出现bug请联系我,我会更新到github上,出bug请pull request。
github:github.com/silky1313/s…