之前springboot用传统连接的方法集成的mqtt,又当生成者又当消费者,经常出现等待来自服务器的响应时超时然后断开后重连也连不上,还出现订阅多个Topic有些Topic消息收不到,用以下方法集成后解决问题
1.依赖
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
</dependency>
2.配置
mqtt:
host: tcp://192.168.0.2:1883
username: admin
password: 123456
topics: mqtt/test/+/Ack,mqtt/test/basic
qos: 0,0
3.配置类
@Data
@Slf4j
@Configuration
@ConfigurationProperties(prefix = "mqtt")
public class MqttConfig {
/**
* 订阅的bean名称
*/
public static final String CHANNEL_NAME_IN = "mqttInboundChannel";
/**
* 发布的bean名称
*/
public static final String CHANNEL_NAME_OUT = "mqttOutboundChannel";
/**
* mqtt地址
*/
private String host;
/**
* mqtt用户名
*/
private String username;
/**
* mqtt密码
*/
private String password;
/**
* 订阅
*/
private String[] topics;
private int[] qos;
/**
* 客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息
*/
private static final byte[] WILL_DATA = "offline".getBytes();
/**
* MQTT连接器选项
*/
@Bean
public MqttConnectOptions getMqttConnectOptions() {
MqttConnectOptions options = new MqttConnectOptions();
// 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,
// 这里设置为true表示每次连接到服务器都以新的身份连接
options.setCleanSession(true);
// 设置连接的用户名
options.setUserName(username);
// 设置连接的密码
options.setPassword(password.toCharArray());
options.setServerURIs(StringUtils.split(host, ","));
// 设置超时时间 单位为秒
options.setConnectionTimeout(10);
// 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送心跳判断客户端是否在线
options.setKeepAliveInterval(20);
// 设置“遗嘱”消息的话题,若客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息。
options.setWill("willTopic", WILL_DATA, 2, false);
//断线重连
options.setAutomaticReconnect(true);
return options;
}
/**
* MQTT客户端
*/
@Bean
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setConnectionOptions(getMqttConnectOptions());
return factory;
}
/**
* MQTT信息通道(生产者)
*/
@Bean(name = CHANNEL_NAME_OUT)
public MessageChannel mqttOutboundChannel() {
return new DirectChannel();
}
/**
* MQTT消息处理器(生产者)
*/
@Bean
@ServiceActivator(inputChannel = CHANNEL_NAME_OUT)
public MessageHandler mqttOutbound() {
MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(
MqttConstant.CLIENT_SUFFIX_PRODUCERS + IdUtils.fastSimpleUUID(),
mqttClientFactory());
//设置默认发送的topic
//messageHandler.setDefaultTopic("mqtt/default");
//messageHandler.setDefaultQos(1);
//异步发送消息
messageHandler.setAsync(true);
return messageHandler;
}
/**
* MQTT消息订阅绑定(消费者)
*/
@Bean
public MessageProducer inbound() {
// 可以同时消费(订阅)多个Topic
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter(
MqttConstant.CLIENT_SUFFIX_CONSUMERS + IdUtils.fastSimpleUUID(), mqttClientFactory(),
topics);
adapter.setCompletionTimeout(5000);
adapter.setConverter(new DefaultPahoMessageConverter());
adapter.setQos(qos);
// 设置订阅通道
adapter.setOutputChannel(mqttInboundChannel());
return adapter;
}
/**
* MQTT信息通道(消费者)
*/
@Bean(name = CHANNEL_NAME_IN)
public MessageChannel mqttInboundChannel() {
return new DirectChannel();
}
/**
* MQTT消息处理器(消费者)
*/
@Bean
@ServiceActivator(inputChannel = CHANNEL_NAME_IN)
public MessageHandler handler() {
return new MqttMessageReceiver();
}
4.常量类
public class MqttConstant {
/**
* 客户端id消费者后缀
*/
public static final String CLIENT_SUFFIX_CONSUMERS = "consumers_";
/**
* 客户端id生产者后缀
*/
public static final String CLIENT_SUFFIX_PRODUCERS = "producers_";
}
5.消费处理器
public class MqttMessageReceiver implements MessageHandler {
/**
* 消息处理
*
* @param message 消息
* @throws MessagingException 消息异常
*/
@Override
public void handleMessage(Message<?> message) throws MessagingException {
// 获取消息Topic
MessageHeaders headers = message.getHeaders();
String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString();
log.info("[获取到的消息的topic]:{} ", topic);
//当客户端接收到订阅的消息时
String payload = (String) message.getPayload();
}
}
6.生产者消息发送接口
@Component
@MessagingGateway(defaultRequestChannel = MqttConfig.CHANNEL_NAME_OUT)
public interface IMqttSender {
/**
* 发送信息到MQTT服务器
* @param data 发送的文本
*/
void sendToMqtt(String data);
/**
* 发送信息到MQTT服务器
* @param topic 主题
* @param payload 消息主体
*/
void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic,
String payload);
/**
* 发送信息到MQTT服务器
*
* @param topic 主题
* @param qos 对消息处理的几种机制
* 0 表示的是订阅者没收到消息不会再次发送,消息会丢失。
* 1 表示的是会尝试重试,一直到接收到消息,但这种情况可能导致订阅者收到多次重复消息。
* 2 多了一次去重的动作,确保订阅者收到的消息有一次。
* @param payload 消息主体
*/
void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic,
@Header(MqttHeaders.QOS) int qos,
String payload);
}
7.其他事件
从Spring 4.2.2开始,当适配器成功订阅到主题了,MqttSubscribedEvent事件就会被触发。当连接失败触发MqttConnectionFailedEvent事件。这两个事件都能够被一个Bean通过实现ApplicationListener而接收到。另外,名为recoveryInterval的新属性控制适配器在失败后尝试重新连接的时间间隔。默认为10000毫秒(10秒)。
@Component
@Slf4j
public class MQTTSubscribedListener implements ApplicationListener<MqttSubscribedEvent> {
@Override
public void onApplicationEvent(MqttSubscribedEvent event) {
log.debug("Subscribed Success: " + event.getMessage());
}
}