在Spring Boot中集成MQTT(Message Queuing Telemetry Transport)是一个常见的需求,尤其是在需要实现物联网(IoT)通信时。MQTT是一种轻量级的、基于发布/订阅模式的消息传输协议,广泛用于低带宽、不可靠或高延迟的网络环境中。
以下是一个基本的步骤指南,介绍如何在Spring Boot项目中集成MQTT:
1. 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
</dependency>
2. 配置MQTT客户端
参数配置
@Data
@Component("mqttConfigProp")
@ConfigurationProperties(prefix = "mqtt")
public class MqttConfigProp {
private String[] serverURIs;
private String clientId;
private String userName;
private char[] password;
private String topic;
private int timeout;
private int keepAlive;
private boolean cleanSession;
}
接收配置
@Slf4j
@Configuration
@EnableIntegration
public class MqttConfigurationInbound {
@Autowired
private MqttConfigProp mqttConfigProp;
@Bean
public MqttConnectOptions mqttConnectOptionInbound() {
MqttConnectOptions options = new MqttConnectOptions();
options.setServerURIs(mqttConfigProp.getServerURIs());
options.setUserName(mqttConfigProp.getUserName());
options.setPassword(mqttConfigProp.getPassword());
options.setKeepAliveInterval(mqttConfigProp.getKeepAlive());
options.setCleanSession(mqttConfigProp.isCleanSession());
return options;
}
@Bean
public MqttPahoClientFactory mqttClientFactoryInbound() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setConnectionOptions(mqttConnectOptionInbound());
return factory;
}
@Bean
public MessageChannel mqttInputChannel() {
return new DirectChannel();
}
@Bean
public MessageProducer brokerMqttInbound() {
MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(
mqttConfigProp.getClientId()+ UUID.randomUUID().toString() + "_inbound",
mqttConnectOptionInbound(), mqttConfigProp.getTopic());
adapter.setConverter(new DefaultPahoMessageConverter());
adapter.setOutputChannel(mqttInputChannel());
adapter.setCompletionTimeout(mqttConfigProp.getTimeout());
adapter.setQos(1);
return adapter;
}
/**
* mqtt连接失败或者订阅失败事件
* @param event
*/
@EventListener(MqttConnectionFailedEvent.class)
public void mqttConnectionFailedEvent(MqttConnectionFailedEvent event) {
log.error("mqttConnectionFailedEvent连接mqtt失败: " +
"date={}, error={}",
new Date(), event.getCause().getMessage());
}
/**
* 当async和async事件(async-events)都为true时,将发出MqttMessageSentEvent
* 它包含消息、主题、客户端库生成的消息id、clientId和clientInstance(每次连接客户端时递增)
* @param event
*/
@EventListener(MqttMessageSentEvent.class)
public void mqttMessageSentEvent(MqttMessageSentEvent event) {
log.info("mqttMessageSentEvent发送信息: date={}, info={}", new Date(), event.toString());
}
/**
* 当async和async事件(async-events)都为true时,将发出MqttMessageDeliveredEvent
* 当客户端库确认传递时,将发出MqttMessageDeliveredEvent。它包含messageId、clientId和clientInstance,使传递与发送相关。
* @param event
*/
@EventListener(MqttMessageDeliveredEvent.class)
public void mqttMessageDeliveredEvent(MqttMessageDeliveredEvent event) {
log.info("mqttMessageDeliveredEvent发送成功信息: date={}, info={}", new Date(), event.toString());
}
/**
* 成功订阅到主题,MqttSubscribedEvent事件就会被触发(多个主题,多次触发)
* @param event
*/
@EventListener(MqttSubscribedEvent.class)
public void mqttSubscribedEvent(MqttSubscribedEvent event) {
log.info("mqttSubscribedEvent订阅成功信息: date={}, info={}", new Date(), event.toString());
}
}
输出配置
@Slf4j
@Configuration
@EnableIntegration
public class MqttConfigurationOutBound {
@Autowired
private MqttConfigProp mqttConfigProp;
@Bean
public MqttConnectOptions mqttConnectOptionsOutBound() {
MqttConnectOptions options = new MqttConnectOptions();
options.setServerURIs(mqttConfigProp.getServerURIs());
options.setUserName(mqttConfigProp.getUserName());
options.setPassword(mqttConfigProp.getPassword());
options.setKeepAliveInterval(mqttConfigProp.getKeepAlive());
options.setCleanSession(mqttConfigProp.isCleanSession());
return options;
}
@Bean
public MqttPahoClientFactory mqttClientFactoryOutBound() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setConnectionOptions(mqttConnectOptionsOutBound());
return factory;
}
@Bean
public MessageChannel mqttOutputChannel() {
return new DirectChannel();
}
@Bean
public MessageHandler mqttOutbound() {
MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(
mqttConfigProp.getClientId()+ UUID.randomUUID().toString() + "_outbound", mqttClientFactoryOutBound());
messageHandler.setAsync(true);
messageHandler.setCompletionTimeout(mqttConfigProp.getTimeout());
return messageHandler;
}
@Bean
public IntegrationFlow mqttOutboundFlow() {
return IntegrationFlows.from(mqttOutputChannel())
.handle(mqttOutbound())
.get();
}
}
消息处理
@Service
@Slf4j
public class MqttHandler implements MessageHandler {
@ServiceActivator(inputChannel = "mqttInputChannel")
@Override
public void handleMessage(Message<?> message) throws MessagingException {
String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC, String.class);
log.info("topic:{}",topic);
Object payload = message.getPayload();
log.info("message info:{}",payload );
}
}
发送消息
@Component
@MessagingGateway(defaultRequestChannel = "mqttOutputChannel")
public interface MqttGateway {
/**
* 发送信息到MQTT服务器
*
* @param topic 主题
* @param message 消息主体
*/
@Gateway(replyTimeout = 2, requestTimeout = 200)
void publishMqttMessageWithTopic(String message, @Header(MqttHeaders.TOPIC) String topic);
/**
* 发送信息到MQTT服务器
*
* @param topic 主题
* @param qos 对消息处理的几种机制。
* 0 表示的是订阅者没收到消息不会再次发送,消息会丢失。
* 1 表示的是会尝试重试,一直到接收到消息,但这种情况可能导致订阅者收到多次重复消息。
* 2 多了一次去重的动作,确保订阅者收到的消息有一次。
* @param message 消息主体
*/
@Gateway(replyTimeout = 2, requestTimeout = 200)
void publishMqttMessageWithTopic(String message, @Header(MqttHeaders.TOPIC) String topic,@Header(MqttHeaders.QOS) int qos);
}