调试工具:MQTTX+EMQX
前置依赖:
<!--mqtt -->
<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>
1. 编写基础配置信息
@ConfigurationProperties("spring.mqtt")
@Component
public class MqttProperties {
private String username;
private String password;
private String hostUrl;
private String clientId;
private String receiveTopic;
private Integer receiveQos = 0;
private String sendTopic = "demo/test";
private Integer sendQos = 0;
private String completionTimeout;
private Integer keepAlive;
}
2. 编写配置类
@Configuration
@Slf4j
public class MqttConfiguration {@Resource
private MqttProperties mqttProperties;
/**
* 事件触发
*/
@Resource
private ApplicationEventPublisher eventPublisher;public MqttConnectOptions getMqttConnectOptions(){
MqttConnectOptions mqttConnectOptions=new MqttConnectOptions();
if(StringUtils.hasText(mqttProperties.getUsername())){
mqttConnectOptions.setUserName(mqttProperties.getUsername());
}
if(StringUtils.hasText(mqttProperties.getPassword())) {
mqttConnectOptions.setPassword(mqttProperties.getPassword().toCharArray());
}
mqttConnectOptions.setServerURIs(mqttProperties.getHostUrl().split(","));
mqttConnectOptions.setKeepAliveInterval(60);
//mqttConnectOptions.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1);
mqttConnectOptions.setKeepAliveInterval(mqttProperties.getKeepAlive());
mqttConnectOptions.setAutomaticReconnect(true); //重连
return mqttConnectOptions;
}
@Bean
public MqttPahoClientFactory mqttClientFactory() {DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setConnectionOptions(this.getMqttConnectOptions());
return factory;
}@Bean
public MessageChannel mqttInputChannel() {
return new DirectChannel();
}/**
* 配置client,监听入栈消息 topic
*/
@Bean
public MessageProducer inbound() {
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter(mqttProperties.getClientId()+"_inbound", mqttClientFactory(),
mqttProperties.getReceiveTopic().split(","));
adapter.setCompletionTimeout(Long.valueOf(mqttProperties.getCompletionTimeout()));
adapter.setConverter(new DefaultPahoMessageConverter());
//默认topic
adapter.addTopic("dsmp/test");
adapter.setQos(mqttProperties.getReceiveQos());
adapter.setOutputChannel(mqttInputChannel());
return adapter;
}@Bean
@ServiceActivator(inputChannel = "mqttInputChannel")
public MessageHandler handler() {
return new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC).toString();
String qos = message.getHeaders().get(MqttHeaders.RECEIVED_QOS).toString();
eventPublisher.publishEvent(new MqttEvent(this, topic, message.getPayload().toString()));
}
};
}/**
* 发送消息和消费消息Channel可以使用相同MqttPahoClientFactory
*
* @return
*/
@Bean
@ServiceActivator(inputChannel = "mqttOutboundChannel")
public MessageHandler mqttOutbound() {
// 在这里进行mqttOutboundChannel的相关设置
MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(mqttProperties.getClientId(), mqttClientFactory());
// 如果设置成true,发送消息时将不会阻塞。
messageHandler.setAsync(true);
messageHandler.setDefaultQos(mqttProperties.getSendQos());
messageHandler.setDefaultTopic(mqttProperties.getSendTopic());
return messageHandler;
}@Bean
public MessageChannel mqttOutboundChannel() {
return new DirectChannel();
}
}
3. 自定义事件
/**
* 自定义mqtt事件
*/
public class MqttEvent extends ApplicationEvent {private String topic;
/**
* 发送的消息
*/
private String message;public MqttEvent(Object source,String topic,String message) {
super(source);
this.topic = topic;
this.message = message;
}public String getTopic() {
return topic;
}public void setTopic(String topic) {
this.topic = topic;
}public String getMessage() {
return message;
}public void setMessage(String message) {
this.message = message;
}
}
4. 消息网关
@Component
@MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
public interface MqttBox {
void sendToMqtt(String data);void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String payload);
void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload);
}
5. 使用
5.1 yml配置:
spring: mqtt: username: password: host-url: tcp://127.0.0.1:1884 client-id: test-demo receive-qos: 0 receive-topic: test1 send-topic: test2 send-qos: 1 completionTimeout: 3000 keepAlive: 6
5.2.1 直接订阅
@Component @Slf4j public class MqttListener { @EventListener(condition = "#mqttEvent.topic.startsWith('test')") public void onEmqttCalll1(MqttEvent mqttEvent) throws Exception { String topic = mqttEvent.getTopic(); //写逻辑处理 Log.info("接收到消test:" + mqttEvent.getMessage()); } @EventListener(condition = "#mqttEvent.topic.equals('abc')") public void onEmqttCalll2(MqttEvent mqttEvent) throws Exception { String topic = mqttEvent.getTopic(); //写逻辑处理 Log.info("接收到消abc:" + mqttEvent.getMessage()); } }
5.2.1 统一监听
public interface ITopicHandle { /** * 判断topic * * @param topic topic * @return */ boolean topicName(String topic); /** * 处理消息 * * @param msg msg */ void handleMsg(String msg); }@Component @Slf4j @RequiredArgsConstructor public class TestTopicHandleImpl implements ITopicHandle { private final MqttBox mqttBox; @Override public boolean topicName(String topic) { return topic.equals(CommonConstant.TEST_TOPIC); } @Override public void handleMsg(String msg) { log.info("【订阅topic】 | topic:{} | 消息内容msg:{}", CommonConstant.TEST_TOPIC, msg); // } }/** * @Author qiuzh * @Description 订阅主题消息 * @Date 2023/11/2 9:16 */ @Component @Slf4j @RequiredArgsConstructor public class MqttListener { private final List<ITopicHandle> topicHandle; @EventListener public void topic(MqttEvent mqttEvent) { this.topicHandle.stream() .filter(provider -> provider.topicName(mqttEvent.getTopic())) .findFirst() .ifPresent(provider -> { provider.handleMsg(mqttEvent.getMessage()); }); } }