cleanSession
为 true 时表示创建一个新的会话,每次连接时不会持久化订阅信息或消息队列。如果连接断开,服务器会丢失客户端的会话信息(例如订阅的主题)。
为 false 时表示创建一个持久会话,在客户端断开连接后会话仍然保持,服务器不会丢失客户端的会话信息(例如订阅的主题),直到会话超时注销。
keepAliveInterval
心跳时间间隔,默认60s
MQTT 协议中约定:在 1.5*Keep Alive 的时间间隔内,如果 Broker 没有收到来自 Client 的任何数据包,那么 Broker 认为它和 Client 之间的连接已经断开;同样地, 如果 Client 没有收到来自 Broker 的任何数据包,那么 Client 认为它和 Broker 之间的连接已经断开。
emqx中可以通过日志追踪查看心跳日志。
clientId
不会安装docker的可以参考文章:linux安装docker和docker-compose详细教程
docker-compose.yml配置
version: '3.3'
services:
mqtt:
image: emqx/emqx:latest
container_name: mqtt_server
ports:
- "1883:1883"
- "8083:8083"
- "18083:18083"
networks:
- mqtt
volumes:
# - ./conf/emqx.conf:/opt/emqx/etc/emqx.conf
# - ./data:/opt/emqx/data
- ./log:/opt/emqx/log
networks:
mqtt:
保存后直接docker-compose up -d
启动,访问页面:http://192.168.80.251:18083/
默认账号:admin 密码:public
gitee项目地址:https://gitee.com/wangyunchao6/springboot-mqtt.git
pom依赖
<dependency>
<groupId>org.eclipse.pahogroupId>
<artifactId>org.eclipse.paho.client.mqttv3artifactId>
<version>1.2.5version>
dependency>
application.yml配置
mqtt:
broker-url: tcp://192.168.80.251:1883 # 替换为你的 MQTT 服务器ip地址
client-id: mqtt-server #可以随便写
username: admin # 如果需要认证
password: public # 如果需要认证
default-topic: test/topic
mqtt连接配置
注意
:订阅主题的时候也可以不调用callback 方法,直接在subscribe中处理业务逻辑。
mqttClient.subscribe(topic,2, (t, msg) -> {
System.out.println("Received message from topic: " + t + ", Message: " + new String(msg.getPayload()));
});
MqttServerConfig,用这个作为mqtt的服务端订阅test/topic主题
package com.example.springbootmqtt.config;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 接收消息的mqtt服务端配置
*/
@Configuration
@Slf4j
public class MqttServerConfig {
@Value("${mqtt.broker-url}")
private String brokerUrl;
@Value("${mqtt.client-id}")
private String clientId;
@Value("${mqtt.username}")
private String username;
@Value("${mqtt.password}")
private String password;
@Bean
public MqttClient mqttClient() throws MqttException {
MqttClient client = new MqttClient(brokerUrl, clientId);
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(username);
options.setPassword(password.toCharArray());
options.setCleanSession(true);
//心跳时间间隔,默认60S
// options.setKeepAliveInterval(30);
//连接超时时间
// options.setConnectionTimeout(10);
//设置自动连接
options.setAutomaticReconnect(true);
client.connect(options);
client.setCallback(new MqttCallback() {
/**
* 当客户端与 MQTT Broker 的连接意外断开时触发此方法。
* 断开的原因会通过参数 cause 传递过来
*/
@Override
public void connectionLost(Throwable cause) {
if (!client.isConnected()) {
try {
client.reconnect();
} catch (MqttException e) {
log.error("connectio lost,Throwable={}",cause.getMessage());
log.error("connectio lost,MqttException={}",e.getMessage());
}
}
}
/**
* 当客户端收到一条消息时触发此方法。
*/
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println("Received message from topic: " + topic);
String payload = new String(message.getPayload());
// Handle message based on topic
switch (topic) {
case "topic1":
break;
case "topic2":
break;
case "topic3":
break;
default:
System.out.println("Unknown topic: " + topic);
break;
}
}
/**
* 当客户端发送的消息成功到达 Broker(仅对 QoS 1 和 QoS 2 消息有效)时触发此方法。
* 用于确认消息已经完成传输。
*/
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
// Not used for subscribers
try {
System.out.println("Delivery complete. Message: " + token.getMessage());
} catch (Exception e) {
e.printStackTrace();
} }
});
//服务启动时订阅主题
client.subscribe("test/topic", 2);
return client;
}
}
MockMqttClientOneConfig,用这个模拟mqtt客户端向test/topic主题发送数据
package com.example.springbootmqtt.config;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 模拟发送消息的mqtt客户端1配置
*/
@Configuration
@Slf4j
public class MockMqttClientOneConfig {
@Value("${mqtt.broker-url}")
private String brokerUrl;
@Value("${mqtt.username}")
private String username;
@Value("${mqtt.password}")
private String password;
@Bean
public MqttClient mockMqttClientOne() throws MqttException {
String clientId = "mock-mqtt-client-one";
MqttClient client = new MqttClient(brokerUrl, clientId);
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(username);
options.setPassword(password.toCharArray());
options.setCleanSession(true);
//心跳时间间隔,默认60S
// options.setKeepAliveInterval(30);
//连接超时时间
// options.setConnectionTimeout(10);
//设置自动连接
options.setAutomaticReconnect(true);
client.connect(options);
client.setCallback(new MqttCallback() {
/**
* 当客户端与 MQTT Broker 的连接意外断开时触发此方法。
* 断开的原因会通过参数 cause 传递过来
*/
@Override
public void connectionLost(Throwable cause) {
if (!client.isConnected()) {
try {
client.reconnect();
} catch (MqttException e) {
log.error("connectio lost,Throwable={}",cause.getMessage());
log.error("connectio lost,MqttException={}",e.getMessage());
}
}
}
/**
* 当客户端收到一条消息时触发此方法。
*/
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println("Received message from topic: " + topic);
String payload = new String(message.getPayload());
// Handle message based on topic
switch (topic) {
case "topic1":
break;
case "topic2":
break;
case "topic3":
break;
default:
System.out.println("Unknown topic: " + topic);
break;
}
}
/**
* 当客户端发送的消息成功到达 Broker(仅对 QoS 1 和 QoS 2 消息有效)时触发此方法。
* 用于确认消息已经完成传输。
*/
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
// Not used for subscribers
try {
System.out.println("Delivery complete. Message: " + token.getMessage());
} catch (Exception e) {
e.printStackTrace();
} }
});
return client;
}
}
MqttController
package com.example.springbootmqtt.controller;
import com.example.springbootmqtt.service.MqttService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/mqtt")
public class MqttController {
@Autowired
private MqttService mqttService;
// 发布消息
@GetMapping("/publish")
public String publish(String topic, String message) {
mqttService.sendMessage(topic, message);
return "Message published to topic: " + topic;
}
// 订阅主题
@GetMapping("/subscribe")
public String subscribe( String topic) {
mqttService.subscribe(topic);
return "Subscribed to topic: " + topic;
}
// 模拟MockMqttClientOne客户端发布消息
@GetMapping("/mockClientOnepublish")
public String mockClientOnepublish( String topic, String message) {
mqttService.mockClientOnepublish(topic, message);
return "mockClientOnepublish Message published to topic: " + topic;
}
// 模拟MockMqttClientTwo客户端发布消息
@GetMapping("/mockClientTwoPublish")
public String mockClientTwoPublish( String topic, String message) {
mqttService.mockClientTwoPublish(topic, message);
return "mockClientTwoPublish Message published to topic: " + topic;
}
}
MqttService
package com.example.springbootmqtt.service;
public interface MqttService {
void sendMessage(String topic, String message);
void mockClientOnepublish(String topic, String message);
void mockClientTwoPublish(String topic, String message);
void subscribe(String topic);
void sendDefaultMessage(String message);
void subscribeDefaultTopic();
}
MqttServiceImpl
package com.example.springbootmqtt.service.impl;
import com.example.springbootmqtt.service.MqttService;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.UUID;
@Slf4j
@Service
public class MqttServiceImpl implements MqttService {
@Autowired
@Qualifier("mqttClient")
private MqttClient mqttClient;
@Autowired
@Qualifier("mockMqttClientOne")
private MqttClient mockMqttClientOne;
@Autowired
@Qualifier("mockMqttClientTwo")
private MqttClient mockMqttClientTwo;
@Value("${mqtt.default-topic}")
private String defaultTopic;
// 发送消息
@Override
public void sendMessage(String topic, String message) {
try {
MqttMessage mqttMessage = new MqttMessage(message.getBytes());
mqttMessage.setQos(1);
mqttClient.publish(topic, mqttMessage);
System.out.println("Message sent to topic: " + topic + ", Message: " + message);
} catch (MqttException e) {
e.printStackTrace();
}
}
@Override
public void mockClientOnepublish(String topic, String message) {
try {
MqttMessage mqttMessage = new MqttMessage(message.getBytes());
mqttMessage.setQos(1);
mockMqttClientOne.publish(topic, mqttMessage);
System.out.println("Message sent to topic: " + topic + ", Message: " + message);
} catch (MqttException e) {
e.printStackTrace();
}
}
@Override
public void mockClientTwoPublish(String topic, String message) {
try {
String clientId = UUID.randomUUID().toString();
MqttClient client = new MqttClient("tcp://192.168.80.251:1883", clientId);
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName("admin");
options.setPassword("public".toCharArray());
options.setCleanSession(true);
//心跳时间间隔,默认60S
// options.setKeepAliveInterval(30);
//连接超时时间
// options.setConnectionTimeout(10);
//设置自动连接
options.setAutomaticReconnect(true);
client.connect(options);
MqttMessage mqttMessage = new MqttMessage(message.getBytes());
mqttMessage.setQos(1);
client.publish(topic, mqttMessage);
System.out.println("Message sent to topic: " + topic + ", Message: " + message);
} catch (MqttException e) {
e.printStackTrace();
}
}
// 订阅消息
@Override
public void subscribe(String topic) {
try {
mqttClient.subscribe(topic, (t, msg) -> {
System.out.println("Received message from topic: " + t + ", Message: " + new String(msg.getPayload()));
});
System.out.println("Subscribed to topic: " + topic);
} catch (MqttException e) {
e.printStackTrace();
}
}
// 默认主题发送消息
@Override
public void sendDefaultMessage(String message) {
sendMessage(defaultTopic, message);
}
// 默认主题订阅
@Override
public void subscribeDefaultTopic() {
subscribe(defaultTopic);
}
}
启动程序,调用接口:http://127.0.0.1:8080/mqtt/mockClientTwoPublish?topic=test/topic&message=aaaaaa
查看打印结果
先说结论:
向主题test/topic发送一条消息,看上图的控制台输出结果,消息发布者并没有进入messageArrived方法,消息订阅者并没有进入deliveryComplete方法,所以在编写代码时只需要根据自己的角色,在对应方法写业务逻辑即可。