记录springboot集成多个mqtt,实现重连后重新订阅

springboot整合多个mqtt

前提:实际应用中,项目需要订阅其他服务器的消息,并且把本机消息发布,所以需要连接多个mqtt,实现不同的需求
参考:https://blog.csdn.net/qq_40083897/article/details/117333716
整合大致结构如下:

1. 引入依赖

 <!-- mqtt -->
 <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配置信息
  mqtt:
    # 设备连接
    channel1:
      username: xxxxxx
      password: xxxxxx
      host: tcp://192.168.xx.xxx:1883
      clientid: device_channel1
      timeout: 1000
      keepalive: 10
      cleanSession: false
    #本地连接
    channel2:
      username: xxxxxx
      password: xxxxxx
      host: tcp://192.168.xx.xxx:1883
      clientid: local_channel2
      timeout: 1000
      keepalive: 10
      cleanSession: false

3.连接配置

@Data
public class MqttConfiguration {

    private String host;
    private String clientId;
    private String userName;
    private String password;
    private String topic;
    private int timeout;
    private int keepAlive;
    private boolean cleanSession;
}

@Slf4j
@Component
@Configuration
@ConfigurationProperties(MqttConfiguration1.PREFIX)
public class MqttConfiguration1 extends MqttConfiguration{

    //指定配置文件application-local.properties中的属性名前缀
    public static final String PREFIX = "spring.mqtt.channel1";

}
@Slf4j
@Component
@Configuration
@ConfigurationProperties(MqttConfiguration2.PREFIX)
public class MqttConfiguration2 extends MqttConfiguration{

    //指定配置文件application-local.properties中的属性名前缀
    public static final String PREFIX = "spring.mqtt.channel2";

}

4.连接管理

@Slf4j
@Component
public class MqttClientConnection {

    private MqttClient mqttClient;

    /**
     * 系统的mqtt客户端id
     */
    private String mqttClientId;


    /**
     * 客户端
     */
    public static ConcurrentHashMap<String, MqttClientConnection> mqttClients = new ConcurrentHashMap();
    /**
     * 客户端connect连接mqtt服务器
     *
     * @param userName     用户名
     * @param passWord     密码
     * @param mqttCallback 回调函数
     **/
    public MqttClient setMqttClient(String host, String clientId, String userName, String passWord, boolean cleanSession, MqttCallback mqttCallback) throws MqttException {
        MqttConnectOptions options = mqttConnectOptions(host, clientId, userName, passWord, cleanSession);
        if (mqttCallback == null) {
            mqttClient.setCallback(new MqttClientCallback(mqttClientId));
        } else {
            mqttClient.setCallback(mqttCallback);
        }
        mqttClient.connect(options);
        return mqttClient;
    }

    /**
     * MQTT连接参数设置
     */
    private MqttConnectOptions mqttConnectOptions(String host, String clientId, String userName, String passWord, boolean cleanSession) throws MqttException {
        mqttClient = new MqttClient(host, clientId, new MemoryPersistence());
        MqttConnectOptions options = new MqttConnectOptions();
        options.setUserName(userName);
        options.setPassword(passWord.toCharArray());
        options.setConnectionTimeout(60);///默认:30
        options.setAutomaticReconnect(true);//默认:false
        options.setCleanSession(cleanSession);//默认:true
        options.setKeepAliveInterval(60);
        return options;
    }

    /**
     * 关闭MQTT连接
     */
    public void close() throws MqttException {
        mqttClient.close();
        mqttClient.disconnect();
    }

    /**
     * 向某个主题发布消息 默认qos:1
     *
     * @param topic:发布的主题
     * @param msg:发布的消息
     */
    public void pub(String topic, String msg) throws MqttException {
        MqttMessage mqttMessage = new MqttMessage();
        //mqttMessage.setQos(2);
        mqttMessage.setPayload(msg.getBytes());
        MqttTopic mqttTopic = mqttClient.getTopic(topic);
        MqttDeliveryToken token = mqttTopic.publish(mqttMessage);
        token.waitForCompletion();
    }

    /**
     * 向某个主题发布消息
     *
     * @param topic: 发布的主题
     * @param msg:   发布的消息
     * @param qos:   消息质量    Qos:0、1、2
     */
    public void pub(String topic, String msg, int qos) throws MqttException {
        MqttMessage mqttMessage = new MqttMessage();
        mqttMessage.setQos(qos);
        mqttMessage.setPayload(msg.getBytes());
        MqttTopic mqttTopic = mqttClient.getTopic(topic);
        MqttDeliveryToken token = mqttTopic.publish(mqttMessage);
        token.waitForCompletion();
    }

    /**
     * 订阅多个主题 ,此方法默认的的Qos等级为:1
     *
     * @param topic 主题
     */
    public void sub(String[] topic) throws MqttException {
        mqttClient.subscribe(topic);
    }

    /**
     * 订阅某一个主题,可携带Qos
     *
     * @param topic 所要订阅的主题
     * @param qos   消息质量:0、1、2
     */
    public void sub(String topic, int qos) throws MqttException {
        mqttClient.subscribe(topic, qos);
    }

    public String getMqttClientId() {
        return mqttClientId;
    }

    public void setMqttClientId(String mqttClientId) {
        this.mqttClientId = mqttClientId;
    }

    public MqttClient getMqttClient(){
        return mqttClient;
    }

}

5.回调函数

@Slf4j
@Component
public class MqttClientCallback implements MqttCallback {

    /**
     * 系统的mqtt客户端id
     */
    private static String mqttClientId;

    public MqttClientCallback(){}
    public MqttClientCallback(String mqttClientId) {
        this.mqttClientId = mqttClientId;
    }
    /**
     * MQTT 断开连接会执行此方法
     */
    @Override
    public void connectionLost(Throwable throwable) {
        log.info("断开了MQTT连接", throwable);
    }

    /**
     * publish发布成功后会执行到这里
     */
    @Override
    public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
        log.info("发布消息成功");
    }

    /**
     * subscribe订阅后得到的消息会执行到这里
     */
    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        //  TODO    此处可以将订阅得到的消息进行业务处理、数据存储
    }

6.项目监听

@Slf4j
@Component
public class MqttClientListener implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    private MqttConfiguration1 mqttConfiguration1;

    @Autowired
    private MqttConfiguration2 mqttConfiguration2;

    private volatile AtomicBoolean isInit = new AtomicBoolean(false);


    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        //防止重复触发
        if (!isInit.compareAndSet(false, true)) {
            return;
        }
        try {
            MqttClientConnection mqttClientConnect1 = new MqttClientConnection();
            mqttClientConnect1.setMqttClientId(mqttConfiguration2.getClientId());
            mqttClientConnect1.setMqttClient(mqttConfiguration2.getHost(), mqttConfiguration2.getClientId(), mqttConfiguration2.getUserName(), mqttConfiguration2.getPassword(), false, new MqttClientCallback(mqttConfiguration2.getClientId()));
            MqttClientConnection.mqttClients.put(mqttConfiguration2.getClientId(), mqttClientConnect1);
            log.info("{}--连接成功!!订阅主题--{}", mqttConfiguration2.getClientId(), mqttConfiguration2.getTopic(), mqttConfiguration2.getKeepAlive(), mqttConfiguration2.getTimeout());
        }catch (Exception e) {
            log.error("本地mqtt连接失败", e);
        }
        try {
            MqttClientConnection mqttClientConnect = new MqttClientConnection();
            mqttClientConnect.setMqttClientId(mqttConfiguration1.getClientId());
            mqttClientConnect.setMqttClient(mqttConfiguration1.getHost(), mqttConfiguration1.getClientId(), mqttConfiguration1.getUserName(), mqttConfiguration1.getPassword(), false, new MqttClientCallback(mqttConfiguration1.getClientId()));
            MqttClientConnection.mqttClients.put(mqttConfiguration1.getClientId(), mqttClientConnect);
            log.info("{}--连接成功!!订阅主题--{}", mqttConfiguration1.getClientId(), mqttConfiguration1.getTopic(),mqttConfiguration1.getKeepAlive(),mqttConfiguration1.getTimeout());
        }catch (Exception e) {
            log.error("连接设备mqtt失败", e);
        }
    }

}

7.实际使用

1、订阅
        MqttClientConnection mqttClientConnection = MqttClientConnection.mqttClients.get("device_channel1");
        MqttClient client = mqttClientConnection.getMqttClient();
        try {
            // 订阅返回值消息
            client.subscribe("mqtt",0, new IMqttMessageListener() {
                @Override
                public void messageArrived(String topic, MqttMessage message) throws Exception {
                    String data = new String(message.getPayload());
                }
            });
        } catch (MqttException e) {
            log.error("mqtt订阅报错",e);
        }
2、发布
        MqttClientConnection mqttClient= MqttClientConnection.mqttClients.get("local_channel2");
        try {
            mqttClient.pub("topic",JSON.toJSONString(data));
        } catch (MqttException e) {
            log.error("mqtt推送消息异常",e);
        }

由此完成了springboot集合多个mqtt。
但是实际应用中又发现,订阅方丢失了数据,一直没有接收到新数据,但是重新订阅又可以,所以定位问题到mqtt进行了重连并且在连接配置中设置了options.setAutomaticReconnect(true);mqtt会断线自动重新连接,但是之前订阅的topic并没有接着接收信息,如果想继续接收信息,必须重新订阅这些topic。
参考:https://blog.csdn.net/paladinzh/article/details/88053530
对回调函数 做了以下调整:

   @Slf4j
@Component
public class MqttClientCallback implements MqttCallback,MqttCallbackExtended {

    /**
     * 系统的mqtt客户端id
     */
    private static String mqttClientId;

    @Autowired
    private MqttConfiguration1 mqttConfiguration1;
    
    @Autowired
    private MqttConfiguration2 mqttConfiguration2;
    
    public MqttClientCallback(){}
    
    public MqttClientCallback(String mqttClientId) {
        this.mqttClientId = mqttClientId;
    }
    @Autowired
    private IAlarmConfigService alarmConfigService;

    @Autowired
    private DeviceMqttSub deviceMqttSub;

    public static MqttClientCallback mqttClientCallback ;
    //通过@PostConstruct实现初始化bean之前进行的操作
    @PostConstruct
    public void init() {
        log.info("初始化");
        mqttClientCallback = this;
        mqttClientCallback.alarmConfigService = this.alarmConfigService;
        mqttClientCallback.deviceMqttSub = this.deviceMqttSub;
        mqttClientCallback.mqttConfiguration1 = this.mqttConfiguration1;
        mqttClientCallback.mqttConfiguration2 = this.mqttConfiguration2;
    }
    /**
     * MQTT 断开连接会执行此方法
     */
    @Override
    public void connectionLost(Throwable throwable) {
        log.info("断开了MQTT连接", throwable);
    }

    /**
     * publish发布成功后会执行到这里
     */
    @Override
    public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
        log.info("发布消息成功");
    }

    /**
     * subscribe订阅后得到的消息会执行到这里
     */
    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        //  TODO    此处可以将订阅得到的消息进行业务处理、数据存储
    }

    @Override
    public void connectComplete(boolean b, String s) {
        if (!b) {
            log.info("第一次连接成功");
        }else{
            // 判断是哪个mqtt重启需要重新订阅
            if (s.equals(mqttClientCallback.mqttConfiguration1.getHost())) {
                log.info("重新订阅所有设备信息");
                List<AlarmConfigVo> alarmConfigVos = mqttClientCallback.alarmConfigService.searchList(new AlarmConfig());
                for (int i = 0; i < alarmConfigVos.size(); i++) {
                    List<AlarmRuleConfigVo> alarmRuleConfigs = alarmConfigVos.get(i).getAlarmRuleConfigs();
                    for (int a = 0; a < alarmRuleConfigs.size(); a++) {
                        if (alarmRuleConfigs.get(a).getStatus() == 1) {
                            List<AlarmConfigDevice> devices = alarmRuleConfigs.get(a).getDevices();
                            for (AlarmConfigDevice alarmConfigDevice : devices) {
                                mqttClientCallback.deviceMqttSub.mattDevice(alarmConfigDevice);
                            }

                        }
                    }
                }
            }
        }
    }
}
  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值