springboot 使用 mqtt

目录

 一、服务如何安装emqx(mqtt)

二、导入依赖

三、配置文件内容

四、配置类

五、用户类

六、消息监听类

七、服务类

八、常量类

代码地址


 一、服务如何安装emqx(mqtt)docker 安装 emqx(mqtt)_282162974838632的博客-CSDN博客docker 安装 emqx(mqtt)https://blog.csdn.net/qq_51767561/article/details/127462070?spm=1001.2014.3001.5501

二、导入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-mqtt</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

三、配置文件内容

spring.main.banner-mode=off
server.compression.enabled=true
# mqtt
mqtt.protocol=tcp
mqtt.host=localhost
mqtt.port=8083
mqtt.url=${mqtt.protocol}://${mqtt.host}:${mqtt.port}
mqtt.username=admin
mqtt.password=admin
mqtt.client.name=mqttClient
mqtt.default.file.persistence.path=${user.dir}/data/mqClient
mqtt.default.subscribe.topic=topic1,topic2
mqtt.connect.timeout=10
mqtt.keep.alive.interval=10
mqtt.qos=2
mqtt.auto.reconnect=true
mqtt.clean.session=true

四、配置类

@Configuration
@Getter
@Slf4j
public class MqttConfiguration {

    @Value("${mqtt.protocol:tcp}")
    private String protocol;

    @Value("${mqtt.url:}")
    private String serverUri;

    @Value("${mqtt.host:localhost}")
    private String host;

    @Value("${mqtt.port:8083}")
    private Integer port;

    @Value("${mqtt.username:admin}")
    private String username;

    @Value("${mqtt.password:guest}")
    private String password;

    @Value("${mqtt.client.name:client}")
    private String clientName;

    @Value("${mqtt.default.subscribe.topic:}")
    private String[] topic;

    @Value("${mqtt.connect.timeout:10}")
    private Integer connectTimeout;

    @Value("${mqtt.keep.alive.interval:10}")
    private Integer keepAliveInterval;

    @Value("${mqtt.qos:2}")
    private Integer qos;

    @Value("${mqtt.default.file.persistence.path}")
    private String persistencePath;

    @Value("${mqtt.clean.session:true}")
    private boolean cleanSession;

    @Value("${mqtt.auto.reconnect:true}")
    private boolean autoReconnect;

    @PostConstruct
    public void initUri() {
        if (!StringUtils.hasText(serverUri)) {
            serverUri = protocol + "://" + host + ":" + port;
        }
        if (!StringUtils.hasText(persistencePath)) {
            persistencePath = System.getProperty("user.dir") + "/data/mqClient";
        }
    }

    /**
     * MQTT连接器选项
     */

    @Bean
    public MqttConnectOptions mqttConnectOptions() {
        MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
        // 设置连接的地址
        mqttConnectOptions.setServerURIs(new String[]{serverUri});
        // 设置连接的用户名
        mqttConnectOptions.setUserName(username);
        // 设置连接的密码
        mqttConnectOptions.setPassword(password.toCharArray());
        // 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,
        // 把配置里的 cleanSession 设为false,客户端掉线后 服务器端不会清除session,
        // 当重连后可以接收之前订阅主题的消息。当客户端上线后会接受到它离线的这段时间的消息
        mqttConnectOptions.setCleanSession(cleanSession);
        // 设置超时时间 单位为秒
        mqttConnectOptions.setConnectionTimeout(connectTimeout);
        //设置自动重新连接
        mqttConnectOptions.setAutomaticReconnect(autoReconnect);
        // 设置会话心跳时间 单位为秒 服务器会每隔10秒的时间向客户端发送心跳判断客户端是否在线
        // 但这个方法并没有重连的机制
        mqttConnectOptions.setKeepAliveInterval(keepAliveInterval);
        // 设置“遗嘱”消息的话题,若客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息。
        mqttConnectOptions.setWill("willTopic", "WILL_DATA".getBytes(), 2, false);
        return mqttConnectOptions;
    }

}

MQTT 设计了 3 个 QoS 等级。

  • QoS 0:消息最多传递一次,如果当时客户端不可用,则会丢失该消息。
  • QoS 1:消息传递至少 1 次。
  • QoS 2:消息仅传送一次。

QoS 0 是一种 "fire and forget" 的消息发送模式:Sender (可能是 Publisher 或者 Broker) 发送一条消息之后,就不再关心它有没有发送到对方,也不设置任何重发机制。

QoS 1 包含了简单的重发机制,Sender 发送消息之后等待接收者的 ACK,如果没收到 ACK 则重新发送消息。这种模式能保证消息至少能到达一次,但无法保证消息重复。

QoS 2 设计了重发和重复消息发现机制,保证消息到达对方并且严格只到达一次。

五、用户类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {

    private String name;

    private String description;

    @JsonDeserialize(using = LocalDateTimeDeserializer.class)
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime createBy;


}

六、消息监听类

@Slf4j
public class DefaultMessageListener implements IMqttMessageListener {

    private final String clientName;

    public DefaultMessageListener(String clientName) {
        this.clientName = clientName;
    }

    /**
     * 处理消息
     *
     * @param topic       主题
     * @param mqttMessage 消息
     */
    @Override
    public void messageArrived(String topic, MqttMessage mqttMessage) {
        ObjectMapper mapper = new ObjectMapper();
        try {
            log.info(String.format("MQTT[" + clientName + "] 消息[" + mqttMessage.getId() + "]: 订阅主题[%s]发来消息[%s]", topic, mapper.readValue(mqttMessage.getPayload(), User.class)));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

七、服务类

@Service
@Slf4j
public class MqttService {

    @Autowired
    MqttConfiguration configuration;

    @Autowired
    MqttConnectOptions connectOptions;

    /**
     * 创建 mqtt 客户端
     */
    public String createMqttClient(String clientId) {
        if (isDuplicate(clientId)) {
            return "用户:" + clientId + "已建立连接";
        }
        MqttClient client = null;
        try {
            String clientName = configuration.getClientName() + "-" + clientId;
            // 配置 mqtt 持久性数据存储位置
            MqttDefaultFilePersistence persistence = new MqttDefaultFilePersistence(configuration.getPersistencePath());
            client = new MqttClient(configuration.getServerUri(), clientName, persistence);
            client.connect(connectOptions);
            initClientSubscribe(client, clientName);
            StringPool.MQTT_CLIENTS.put(clientId, client);
        } catch (MqttException e) {
            log.error(String.format("MQTT: 客户端[%s]连接消息服务器[%s]失败", clientId, configuration.getServerUri()));
            e.printStackTrace();
        }
        return "用户:" + clientId + "建立连接成功";
    }

    /**
     * 删除 mqtt 客户端
     */
    public String closeMqttClient(String clientId) {
        if (!isDuplicate(clientId)) {
            return "用户:" + clientId + "当前未建立连接";
        }
        try {
            MqttClient client = getClient(clientId);
            client.disconnect();
            client.close();
            StringPool.MQTT_CLIENTS.remove(clientId);
        } catch (MqttException e) {
            log.error("删除客户端{}失败", clientId);
            e.printStackTrace();
        }
        return "用户:" + clientId + "关闭连接成功";
    }

    /**
     * 发送消息
     *
     * @param clientId 发送信息的客户端的id
     * @param topic    主题
     * @param data     消息内容
     */
    public void publish(String clientId, String topic, Object data) {
        ObjectMapper mapper = new ObjectMapper();
        try {
            // 转换消息为json字符串
            byte[] bytes = mapper.writeValueAsBytes(data);
            MqttMessage mqttMessage = new MqttMessage(bytes);
            mqttMessage.setQos(configuration.getQos());
            getClient(clientId).publish(topic, mqttMessage);
        } catch (JsonProcessingException e) {
            log.error(String.format("MQTT: 主题[%s]发送消息转换json失败", topic));
        } catch (MqttException e) {
            log.error(String.format("MQTT: 主题[%s]发送消息失败", topic));
        }
    }

    /**
     * 订阅主题
     *
     * @param clientId 客户端的id
     * @param topic    主题
     */
    public void subscribe(String clientId, String topic) {
        DefaultMessageListener listener = new DefaultMessageListener(configuration.getClientName());
        subscribe(clientId, topic, listener);
    }

    /**
     * 订阅主题
     *
     * @param clientId 客户端的id
     * @param topic    主题
     * @param listener 消息监听处理器
     */
    public void subscribe(String clientId, String topic, IMqttMessageListener listener) {
        try {
            getClient(clientId).subscribe(topic, configuration.getQos(), listener);
        } catch (MqttException e) {
            log.error(String.format("MQTT: 订阅主题[%s]失败", topic));
        }
    }

    /**
     * 取消订阅主题
     *
     * @param clientId 客户端的id
     * @param topic    主题
     */
    public void unsubscribe(String clientId, String topic) {
        try {
            getClient(clientId).unsubscribe(topic);
        } catch (MqttException e) {
            log.error(String.format("MQTT: 订阅主题[%s]失败", topic));
        }
    }

    /**
     * 检查当前客户端是否已建立连接
     */
    private boolean isDuplicate(String clientId) {
        return StringPool.MQTT_CLIENTS.containsKey(clientId);
    }

    /**
     * 获取客户端
     */
    private MqttClient getClient(String clientId) {
        return Optional.ofNullable(StringPool.MQTT_CLIENTS.get(clientId)).orElseThrow(() -> new RuntimeException("用户:" + clientId + "不存在"));
    }

    /**
     * 初始化客户端的订阅
     */
    private void initClientSubscribe(MqttClient client, String clientName) {
        try {
            int topicLen = configuration.getTopic().length;
            IMqttMessageListener[] listeners = new IMqttMessageListener[topicLen];
            int[] qos = new int[topicLen];
            for (int i = 0; i < topicLen; i++) {
                listeners[i] = new DefaultMessageListener(clientName);
                qos[i] = configuration.getQos();
            }
            client.subscribe(configuration.getTopic(), qos, listeners);
        } catch (MqttException e) {
            log.error(String.format("MQTT: 订阅主题 %s 失败", Arrays.toString(configuration.getTopic())));
            e.printStackTrace();
        }
    }
}

八、常量类

public class StringPool {

    public final static ConcurrentHashMap<String, MqttClient> MQTT_CLIENTS = new ConcurrentHashMap<>();

}

代码地址:

GitHub - 3154834560/mqtt_test_1Contribute to 3154834560/mqtt_test_1 development by creating an account on GitHub.https://github.com/3154834560/mqtt_test_1

自己记录的一些笔记,内容如有不对请指正

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wangjingyang2020

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值