• SpringBoot集成MQTT的步骤和注意事项


    最近项目用到了mqtt,所以记录下SpringBoot集成MQTT的步骤和注意事项,整理一下知识,方便自己和他人。

    一、pom文件里引入maven依赖jar包

    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-mqtt</artifactId>
        <version>5.3.2.RELEASE</version>
    </dependency>

    二、在application.yml配置文件里加入mqtt配置信息

    snake:
      server:
        mqttAddr: 162.11.22.33:1883
        mqttUser: admin
        mqttPwd: 111111

    三、新建配置映射类SnakeServerProperties

    @Data
    @Component
    @ConfigurationProperties(prefix = "snake.server")
    public class SnakeServerProperties {
    
        private String mqttPwd;
        private String mqttAddr;
        private String mqttUser;
    }

    四、新建mqtt连接的工厂类MqttFactory

    里面会有一个getInstance方法,用来构造MqttClient。

    这边需要注意的有两点:

    1.这边要考虑mqtt断开自动重连的情况。
    2.如果要分布式部署,clientId不能设置成一样的,不然会导致多个实例相同mqttClient抢占连接。这会导致明明获取到mqttClient,但发送消息却报异常。

    import com.alibaba.fastjson.JSONObject;
    import lombok.extern.slf4j.Slf4j;
    import org.eclipse.paho.client.mqttv3.MqttClient;
    import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
    import org.eclipse.paho.client.mqttv3.MqttException;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.HashMap;
    import java.util.concurrent.ConcurrentHashMap;
    
    /**
     * @Author: 夏威夷8080
     * @Date: 2022/8/12 15:38
     */
    @Slf4j
    @Component
    public class MqttFactory {
    
        private ConcurrentHashMap<String, MqttClient> clientMap = new ConcurrentHashMap<>();
    
        @Autowired
        private SnakeServerProperties snakeServerProperties;
    
    
        /**
         * 初始化客户端
         */
        public MqttClient getInstance(String clientId) {
            MqttClient client = null;
            String key = clientId;
            if (clientMap.get(key) == null) {
                try {
                    client = new MqttClient("tcp://" + snakeServerProperties.getMqttAddr(), clientId);
                    // MQTT配置对象
                    MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
                    // 设置自动重连, 其它具体参数可以查看MqttConnectOptions
                    mqttConnectOptions.setAutomaticReconnect(true);
                    // 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接
                    // mqttConnectOptions.setCleanSession(true);
                    // 设置超时时间 单位为秒
                    mqttConnectOptions.setConnectionTimeout(10);
                    mqttConnectOptions.setUserName(snakeServerProperties.getMqttUser());
                    mqttConnectOptions.setPassword(snakeServerProperties.getMqttPwd().toCharArray());
                    // mqttConnectOptions.setServerURIs(new String[]{url});
                    // 设置会话心跳时间 单位为秒
                    mqttConnectOptions.setKeepAliveInterval(10);
                    // 设置“遗嘱”消息的话题,若客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息。
                    // mqttConnectOptions.setWill("willTopic", "offline".getBytes(), 2, false);
    
                    if (!client.isConnected()) {
                        client.connect(mqttConnectOptions);
                    }
                    log.info("MQTT创建client成功={}", JSONObject.toJSONString(client));
                    clientMap.put(key, client);
                } catch (MqttException e) {
                    log.error("MQTT连接消息服务器[{}]失败", key + "-" + snakeServerProperties.getMqttAddr());
                }
            } else {
                client = clientMap.get(key);
                log.info("MQTT从map里获取到client={}", JSONObject.toJSONString(client));
                if (!client.isConnected()) {
                    // 如果缓存里的client已经断开,则清除该缓存,再重新创建客户端连接
                    clientMap.remove(key);
                    this.getInstance(clientId);
                }
            }
            return client;
        }
    
    }

    五、新建MqttTemplate模板类

    里面定义了发送消息和订阅消息两个方法。

    import com.alibaba.fastjson.JSONObject;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.base.Throwables;
    import lombok.extern.slf4j.Slf4j;
    import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
    import org.eclipse.paho.client.mqttv3.MqttClient;
    import org.eclipse.paho.client.mqttv3.MqttException;
    import org.eclipse.paho.client.mqttv3.MqttMessage;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.nio.charset.StandardCharsets;
    
    /**
     * @Author: 夏威夷8080
     * @Date: 2022/8/12 15:47
     */
    @Slf4j
    @Component
    public class MqttTemplate {
    
        @Autowired
        private MqttFactory mqttFactory;
    
        /**
         * 发送消息
         *
         * @param topic 主题
         * @param data  消息内容
         */
        public void send(String clientId, String topic, Object data) {
            // 获取客户端实例
            MqttClient client = mqttFactory.getInstance(clientId);
            try {
                // 转换消息为json字符串
                String json = JSONObject.toJSONString(data);
                log.info("MQTT主题[{}]发送消息...\r\n{}", topic, json);
                client.publish(topic, new MqttMessage(json.getBytes(StandardCharsets.UTF_8)));
            } catch (MqttException e) {
                log.error("MQTT主题[{}]发送消息失败,{}", topic, Throwables.getStackTraceAsString(e));
            }
        }
    
        /**
         * 订阅主题
         *
         * @param topic    主题
         * @param listener 消息监听处理器
         */
        public void subscribe(String clientId, String topic, IMqttMessageListener listener) {
            MqttClient client = mqttFactory.getInstance(clientId);
            try {
                log.info("MQTT订阅主题[{}]...", topic);
                client.subscribe(topic, listener);
            } catch (MqttException e) {
                log.error("MQTT订阅主题[{}]失败,{}", topic, Throwables.getStackTraceAsString(e));
            }
        }
    
    }

    六、新建业务操作MQTT处理类MyHandler

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.PostConstruct;
    
    /**
     * @Author: 夏威夷8080
     * @Date: 2022/8/12 15:59
     */
    @Component
    public class MyHandler {
    
        @Autowired
        private MqttTemplate mqttTemplate;
    
        public void listener(String clientId, String topic) {
            mqttTemplate.subscribe(clientId, topic, new SnakeMqttMessageListener());
        }
    
        public void send(String clientId, String topic, CmdRequest cmdRequest) {
            mqttTemplate.send(clientId, topic, cmdRequest);
        }
    
    }

    七、新建自己的mqtt消息监听处理类,需要实现IMqttMessageListener接口

    这边无法直接注入bean,需要从applicationContext里拿

    import cn.hutool.json.JSONUtil;
    import com.alibaba.fastjson.JSON;
    import lombok.extern.slf4j.Slf4j;
    import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
    import org.eclipse.paho.client.mqttv3.MqttMessage;
    import org.springframework.context.ApplicationContext;
    import org.springframework.stereotype.Component;
    
    /**
     * @Author: 夏威夷8080
     * @Date: 2022/8/12 15:50
     */
    @Slf4j
    public class SnakeMqttMessageListener implements IMqttMessageListener {
    
        /**
         * 处理消息
         *
         * @param topic       主题
         * @param mqttMessage 消息
         */
        @Override
        public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
            log.info("MQTT实时订阅主题[{}]发来消息[{}]", topic, new String(mqttMessage.getPayload()));
            ApplicationContext applicationContext = SpringContextHolder.getApplicationContext();
            xxxxxxx xxx = applicationContext.getBean(xxxx.class);
        }
    
    }

    八、顺便把工具类也贴出来SpringContextHolder

    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.context.ApplicationEvent;
    import org.springframework.context.annotation.Lazy;
    import org.springframework.stereotype.Service;
    
    /**
     * @author 夏威夷8080
     * @date 2018/6/27 Spring 工具类
     */
    @Slf4j
    @Service
    @Lazy(false)
    public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
    
        private static ApplicationContext applicationContext = null;
    
        /**
         * 取得存储在静态变量中的ApplicationContext.
         */
        public static ApplicationContext getApplicationContext() {
            return applicationContext;
        }
    
        /**
         * 实现ApplicationContextAware接口, 注入Context到静态变量中.
         */
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) {
            SpringContextHolder.applicationContext = applicationContext;
        }
    
        /**
         * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
         */
        @SuppressWarnings("unchecked")
        public static <T> T getBean(String name) {
            return (T) applicationContext.getBean(name);
        }
    
        /**
         * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
         */
        public static <T> T getBean(Class<T> requiredType) {
            return applicationContext.getBean(requiredType);
        }
    
        /**
         * 清除SpringContextHolder中的ApplicationContext为Null.
         */
        public static void clearHolder() {
            if (log.isDebugEnabled()) {
                log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
            }
            applicationContext = null;
        }
    
        /**
         * 发布事件
         * @param event
         */
        public static void publishEvent(ApplicationEvent event) {
            if (applicationContext == null) {
                return;
            }
            applicationContext.publishEvent(event);
        }
    
        /**
         * 实现DisposableBean接口, 在Context关闭时清理静态变量.
         */
        @Override
        public void destroy() {
            SpringContextHolder.clearHolder();
        }
    
    }

    OK,SpringBoot集成MQTT的步骤和注意事项到这边就介绍完了,希望对大家有帮助,有问题可以在下面留言。

  • 相关阅读:
    【读书笔记】iOS-简单的数据驱动程序
    数据结构—单链表(类C语言描写叙述)
    使用Hadoop ACL 控制訪问权限
    Iocomp控件之数字显示【图文】
    维护的JSP站点数据丢失
    Simditor用法
    Android实战简易教程-第二十六枪(基于ViewPager实现微信页面切换效果)
    Deferred Rendering(三)反锯齿和半透明问题
    iOS Code Sign error: Provisioning profile can&#39;t be found 解决方式
    spring
  • 原文地址:https://www.cnblogs.com/shamo89/p/16813785.html
Copyright © 2020-2023  润新知