mqtt动态订阅。动态增加订阅,过期自动取消订阅
1.通过controller 测试 动态增加订阅 和发送消息
package com.example.demo.mqtt.local;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/mqtt")
@Slf4j
public class Test1 {
@Resource
LocalMqttService localMqttService;
@Resource
LocalMqttSendService localMqttSendService;
@GetMapping("/add")
public void addTopic(@RequestParam("topic") String topic){
localMqttService.addTopic(topic,0);
}
@GetMapping("/send")
public void send(@RequestParam("topic") String topic){
JSONObject jsonObject=new JSONObject();
jsonObject.put("time",System.currentTimeMillis());
jsonObject.put("topic",topic);
localMqttSendService.sendToMqtt(topic,0,jsonObject.toJSONString());
log.info(jsonObject.toJSONString());
}
}
2.mqtt的配置类
package com.example.demo.mqtt.local;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
@Configuration
public class LocalMqttConfig {
//订阅通道的bean名称
public static final String CHANNEL_NAME_IN = "LocalMqttInboundChannel";
//发布通道的bean名称
public static final String CHANNEL_NAME_OUT = "LocalMqttOutboundChannel";
public static final String PRODUCER_MESSAGE_HANDLE = "LocalMqttMessageHandler";
//消费者的bean名称
public static final String CONSUMER_NAME = "LocalMqttConsumer";
//消费者处理handler的bean名称
public static final String CONSUMER_HANDLER_NAME = "LocalMqttHandler";
public static final String PRODUCER_NAME = "LocalMqttProducer";
@Value("${mqtt.local.username}")
private String username;
@Value("${mqtt.local.password}")
private String password;
@Value("${mqtt.local.url}")
private String url;
@Value("${mqtt.local.producerClientId}")
private String producerClientId;
@Value("${mqtt.local.consumerClientId}")
private String consumerClientId;
@Value("${mqtt.local.consumerTopic}")
private String consumerTopic;
@Autowired
private LocalMqttReceiveService mqttReceiveService;
/**
* MQTT连接器选项
*
* @return {@link MqttConnectOptions}
*/
public MqttConnectOptions getMqttConnectOptions() {
MqttConnectOptions options = new MqttConnectOptions();
// 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,
// 这里设置为true表示每次连接到服务器都以新的身份连接
options.setCleanSession(true);
// 设置连接的用户名
options.setUserName(username);
// 设置连接的密码
options.setPassword(password.toCharArray());
// 设置连接地址,多个用“,”隔开
options.setServerURIs(StringUtils.split(url, ","));
// 设置超时时间 单位为秒
options.setConnectionTimeout(10);
// 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送心跳判断客户端是否在线,但这个方法并没有重连的机制
options.setKeepAliveInterval(20);
// 设置“遗嘱”消息的话题,若客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息。
//options.setWill("willTopic", WILL_DATA, 2, false);
return options;
}
/**
* MQTT客户端
*
* @return {@link MqttPahoClientFactory}
*/
public MqttPahoClientFactory mqttClientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setConnectionOptions(getMqttConnectOptions());
return factory;
}
/**
* MQTT信息通道(生产者)
*
* @return {@link MessageChannel}
*/
@Bean(name = CHANNEL_NAME_OUT)
public MessageChannel mqttOutboundChannel() {
return new DirectChannel();
}
/**
* MQTT消息处理器(生产者)
*
* @return {@link MessageHandler}
*/
@Bean(name = PRODUCER_MESSAGE_HANDLE)
@ServiceActivator(inputChannel = CHANNEL_NAME_OUT)
public MessageHandler mqttOutbound() {
String cid = producerClientId + "UUIDGenerator.getUUID16()";
MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(
cid,
mqttClientFactory());
messageHandler.setAsync(true);
return messageHandler;
}
/**
* MQTT消息订阅绑定(消费者)
*
* @return {@link MessageProducer}
*/
@Bean(name = CONSUMER_NAME)
public MqttPahoMessageDrivenChannelAdapter inbound() {
String[] topics = StringUtils.split(consumerTopic, ",");
String cid = consumerClientId + "UUIDGenerator.getUUID16()";
// 可以同时消费(订阅)多个Topic
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter(
cid, mqttClientFactory(), topics);
DefaultPahoMessageConverter converter = new DefaultPahoMessageConverter();
converter.setPayloadAsBytes(true);
adapter.setConverter(converter);
adapter.setQos(0);
// 设置订阅通道
adapter.setOutputChannel(mqttInboundChannel());
return adapter;
}
/**
* MQTT信息通道(消费者)
*
* @return {@link MessageChannel}
*/
@Bean(name = CHANNEL_NAME_IN)
public MessageChannel mqttInboundChannel() {
return new DirectChannel();
}
/**
* MQTT消息处理器(消费者)
*
* @return {@link MessageHandler}
*/
@Bean(name = CONSUMER_HANDLER_NAME)
@ServiceActivator(inputChannel = CHANNEL_NAME_IN)
public MessageHandler handler() {
return message -> mqttReceiveService.receiveNotification(message);
}
}
3.mqtt的消息处理器
@Component
@Slf4j
public class LocalMqttReceiveService {
public void receiveNotification(Message<?> message) {
Optional.ofNullable(message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC))
.map(Objects::toString)
.ifPresent(log::error);
}
}
4.通过通道适配器动态订阅topic
通过定时任务 取消订阅
@Service
@Slf4j
public class LocalMqttService {
@Resource
private LocalMqttSendService localMqttSendService;
@Resource(name = LocalMqttConfig.CONSUMER_NAME)
private MqttPahoMessageDrivenChannelAdapter mqttPahoMessageDrivenChannelAdapter;
private final Map<String, Long> topicMap = new ConcurrentHashMap<>();
public void addTopic(String topic,int qos) {
if (!topicMap.containsKey(topic)) {
mqttPahoMessageDrivenChannelAdapter.addTopic(topic, qos);
}
topicMap.put(topic, System.currentTimeMillis());
}
public void sendMessage(String topic, int qos, String payload) {
localMqttSendService.sendToMqtt(topic, qos, payload);
}
public void sendMessage(String topic, int qos, byte[] payload) {
localMqttSendService.sendToMqtt(topic, qos, payload);
}
/**
* 过期队列,超过时间,则取消订阅topic
*/
@Scheduled(fixedRate = 60000)
public void scheduledTask() {
if (!topicMap.isEmpty()) {
long baseLine = System.currentTimeMillis() - 300000;
Iterator<Map.Entry<String,Long>> iterable = topicMap.entrySet().iterator();
while (iterable.hasNext()) {
Map.Entry<String,Long> e = iterable.next();
long time = e.getValue();
if (baseLine > time) {
String topic = e.getKey();
mqttPahoMessageDrivenChannelAdapter.removeTopic(topic);
iterable.remove();
log.info("mqtt订阅的topic,过期时间5分钟,本次移除的topic="+topic);
}
}
}
}
}