物联网消息队列客户端-MQTT-基本功能实现

简介: 物联网消息队列客户端-MQTT-基本功能实现

1. 主要实现功能

  • 自动配置
  • 消息自动解析
  • 消息分组共享订阅
  • 消息不分组共享订阅
  • 消息排它订阅
  • 延时发布
  • 多数据源

4. 快速开始

4.1 引入依赖

    <dependency>
            <groupId>org.eclipse.paho</groupId>
            <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
            <version>1.2.5</version>
        </dependency>

4.2 配置

spring:
  mqtt:
    emq:
      client:
        # 多数据源客户端名称,默认default
        default:
          # broker地址
          host: 127.0.0.1
          # 端口
          port: 31883
          # 用户名
          username: admin
          # 密码
          password: 123456

更多配置如下

spring:
  mqtt:
    emq:
      client:
        # 多数据源客户端名称,默认default
        default:
          # broker地址
          host: 127.0.0.1
          # 端口
          port: 31883
          # 用户名
          username: admin
          # 密码
          password: 123456
          # 客户端标识,需保持全局唯一
          client-id: parking_server
          # 是否清除session
          clean-session: false
          # 连接超时时间,单位秒
          connection-timeout: 10
          # 心跳间隔时间,单位秒
          keep-alive-interval: 10
          # 全局消息质量
          global-qos: 1
          # 重新连接之间等待的最长时间
          maxReconnect-delay: 128000
          # 是否自动重新连接
          automatic-reconnect: true
          # 最大消息并发数量,超过此数量并发时可能会丢消息
          maxInflight: 1000

4.3 开启自动配置

在启动类上增加@EnableRabbitMqAutoConfiguration注解

import com.demo.mqttclient.anno.EnableEmqAutoConfiguration;
@SpringBootApplication
@EnableEmqAutoConfiguration
public class TestApplication {
    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
}

4.4 发布消息

在生产者的业务程序中,注入MQTTClient

import com.demo.mqttclient.MQTTClient;
@Resource
private MQTTClient defaultMQTTClient;

为了兼容第三方及优化内部使用逻辑,所以内置提供了两种消息发送方式。

4.4.1 第三方消息发送

public String publishHeartbeatReply() {
    HeartbeatReplyMessage heartbeatReplyMessage = new HeartbeatReplyMessage();
    heartbeatReplyMessage.setCmd(32896);
    heartbeatReplyMessage.setExpire(1605252875L);
    heartbeatReplyMessage.setDevid("095437323930030130523933");
    heartbeatReplyMessage.setServer_time("1605252875");
    defaultMQTTClient.publish2ThirdParty("npt/park/type1/dev/095437323930030130523933", 1, heartbeatReplyMessage);
    return "success";
}

4.4.2 内部消息发送

消息实体实现Message接口

package com.example.test.message;
import com.demo.mqttclient.MQTTMessage;
import lombok.Data;
import java.util.UUID;
@Data
public class demoMessage implements MQTTMessage {
    private String msgId = UUID.randomUUID().toString();
    private String name;
    private String gender;
    @Override
    public String getMsgId() {
        return this.msgId;
    }
}

然后直接调用该类的publish方法发送即可

@GetMapping("demo/publish")
public String demoPublish() {
    demoMessage demoMessage = new demoMessage();
    demoMessage.setName("点都");
    demoMessage.setGender("xx");
    defaultMQTTClient.publish("demo/topic", demoMessage);
    return "success";
}

其中存在多个重载的方法。

package com.demo.mqttclient;
import com.demo.mqttclient.enums.ShareModelEnum;
import com.demo.plugin.core.lang.json.JSONUtil;
import org.eclipse.paho.client.mqttv3.MqttMessage;
/**
 * mqtt客户端
 *
 * @author zhangliuyang
 * @date 2022/07/18
 * @since 1.0.0
 */
public interface MQTTClient {
    /**
     * 启动客户端
     */
    void start();
    /**
     * 关闭客户端
     */
    void close();
    /**
     * 发布
     *
     * @param topic   主题
     * @param message 消息
     */
    default <T extends MQTTMessage> void publish(String topic, T message) {
        this.publish(topic, message, 1);
    }
    /**
     * 发布
     *
     * @param topic   主题
     * @param message 消息
     * @param qos     消息质量
     */
    default <T extends MQTTMessage> void publish(String topic, T message, int qos) {
        this.publish(topic, message, qos, 0);
    }
    /**
     * 发布
     *
     * @param topic   主题
     * @param message 消息
     * @param qos     消息质量
     * @param delay   延迟时间[unit:s, max:4294967s, condition: > 0]
     */
    default <T extends MQTTMessage> void publish(String topic, T message, int qos, long delay) {
        this.publish(topic, message, qos, delay, false);
    }
    /**
     * 发布
     *
     * @param topic    主题
     * @param message  消息
     * @param qos      消息质量
     * @param delay    延迟时间[unit:s, max:4294967s, condition: > 0]
     * @param retained 保留消息
     */
    default <T extends MQTTMessage> void publish(String topic, T message, int qos, long delay, boolean retained) {
        MQTTMessageContext mqttMessageContext = new MQTTMessageContext();
        mqttMessageContext.setId(message.getMsgId());
        mqttMessageContext.setPayload(JSONUtil.write(message));
        mqttMessageContext.setQos(qos);
        mqttMessageContext.setDelay(delay);
        mqttMessageContext.setRetained(retained);
        mqttMessageContext.setTimestamp(System.currentTimeMillis());
        this.publish(topic, mqttMessageContext);
    }
    /**
     * 发布
     *
     * @param topic          主题
     * @param messageContext 消息上下文
     */
    void publish(String topic, MQTTMessageContext messageContext);
    /**
     * 发送到第三方
     *
     * @param topic   主题
     * @param message 消息
     */
    default void publish2ThirdParty(String topic, Object message) {
        this.publish2ThirdParty(topic, 1, message);
    }
    /**
     * 发送到第三方
     *
     * @param topic   主题
     * @param qos     消息质量
     * @param message 消息
     */
    default void publish2ThirdParty(String topic, int qos, Object message) {
        this.publish2ThirdParty(topic, qos, message, Constant.DEFAULT_CHARSET.name());
    }
    /**
     * 发送到第三方
     *
     * @param topic       主题
     * @param qos         消息质量
     * @param message     消息
     * @param charsetName 字符集名称
     */
    default void publish2ThirdParty(String topic, int qos, Object message, String charsetName) {
        this.publish2ThirdParty(topic, qos, message, charsetName, 0);
    }
    /**
     * publish2第三方
     *
     * @param topic       主题
     * @param qos         qos
     * @param message     消息
     * @param charsetName 字符集名称
     * @param delay       延迟时间
     */
    default void publish2ThirdParty(String topic, int qos, Object message, String charsetName, long delay) {
        this.publish2ThirdParty(topic, qos, message, charsetName, delay, false);
    }
    /**
     * publish2第三方
     *
     * @param topic       主题
     * @param qos         qos
     * @param message     消息
     * @param charsetName 字符集名称
     * @param delay       延迟时间
     * @param retained    是否保留消息
     */
    default void publish2ThirdParty(String topic, int qos, Object message, String charsetName, long delay, boolean retained) {
        this.publish2ThirdParty(topic, qos, JSONUtil.toString(message).getBytes(charsetName), delay, retained);
    }
    /**
     * 发送到第三方
     *
     * @param topic   主题
     * @param payload 有效载荷
     */
    default void publish2ThirdParty(String topic, byte[] payload) {
        this.publish2ThirdParty(topic, 1, payload, 0);
    }
    /**
     * 发送到第三方
     *
     * @param topic   主题
     * @param qos     消息质量
     * @param payload 有效载荷
     */
    default void publish2ThirdParty(String topic, int qos, byte[] payload) {
        this.publish2ThirdParty(topic, qos, payload, 0);
    }
    /**
     * 发送到第三方
     *
     * @param topic   主题
     * @param qos     消息质量
     * @param payload 有效载荷
     * @param delay   延迟时间
     */
    default void publish2ThirdParty(String topic, int qos, byte[] payload, long delay) {
        this.publish2ThirdParty(topic, qos, payload, delay, false);
    }
    /**
     * 发送到第三方
     *
     * @param topic    主题
     * @param qos      消息质量
     * @param payload  有效载荷
     * @param delay    延迟时间
     * @param retained 是否保留消息
     */
    default void publish2ThirdParty(String topic, int qos, byte[] payload, long delay, boolean retained) {
        MqttMessage mqttMessage = new MqttMessage();
        mqttMessage.setPayload(payload);
        mqttMessage.setQos(qos);
        mqttMessage.setRetained(retained);
        this.publish2ThirdParty(topic, delay, mqttMessage);
    }
    /**
     * 发送到第三方
     *
     * @param topic       主题
     * @param mqttMessage mqtt消息
     * @param delay       延迟时间
     */
    void publish2ThirdParty(String topic, long delay, MqttMessage mqttMessage);
    /**
     * 订阅
     *
     * @param topic          主题
     * @param qos            消息质量
     * @param shareModel     共享模型
     * @param groupName      分组名称
     * @param exclusive      排它
     * @param messageHandler 消息处理程序
     */
    void subscribe(String topic, int qos, ShareModelEnum shareModel, String groupName, boolean exclusive, MessageHandler messageHandler);
}

4.5 接收消息

消费者需要在消息处理类上添加@MQTTSubscriber(topics = {"npt/park/type1/server/10010"})注解,指定要监听topics和客户端名称即可。如果没有显示的指定客户端名称,则使用defaultMQTTClient,使用qos执行订阅消息质量。


当消息处理类中有多个public方法时,需要@MQTTConsumerMethod标记具体消费方法

package com.example.test.handler;
import com.demo.mqttclient.MessageHandler;
import com.demo.mqttclient.anno.MQTTConsumerMethod;
import com.demo.mqttclient.anno.MQTTSubscriber;
import com.demo.mqttclient.enums.ShareModelEnum;
import com.demo.plugin.core.lang.json.JSONUtil;
import com.example.test.message.DeviceStartMessage;
import com.example.test.message.PlateRecognitionReportMessage;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import java.util.Map;
@Slf4j
@MQTTSubscriber(topics = ParkingMessageHandler.TOPIC, qos = 1, share = ShareModelEnum.GROUP_SHARE)
public class ParkingMessageHandler {
    public static final String TOPIC = "npt/park/type1/server/10010";
    public static final String CMD_KEY = "cmd";
    @MQTTConsumerMethod
    public void handle(Map<String, Object> message) {
        log.info("handle:{}", message);
        if (message.containsKey(CMD_KEY)) {
            Integer cmd = (Integer) message.get(CMD_KEY);
            switch (cmd) {
                case 129:
                    handleDeviceStartMessage(message);
                    break;
                case 140:
                    handlePlateRecognitionReportMessage(message);
                    break;
                default:
                    log.warn("不支持此cmd:[{}]", cmd);
                    break;
            }
        } else {
            log.warn("消息消费异常");
        }
    }
    private void handleDeviceStartMessage(Map<String, Object> message) {
        DeviceStartMessage deviceStartMessage = JSONUtil.toObject(JSONUtil.toString(message), DeviceStartMessage.class);
        log.info("接收到设备启动消息:{}", deviceStartMessage);
    }
    private void handlePlateRecognitionReportMessage(Map<String, Object> message) {
        PlateRecognitionReportMessage plateRecognitionReportMessage = JSONUtil.toObject(JSONUtil.toString(message), PlateRecognitionReportMessage.class);
        log.info("接收到车牌上报识别消息:{}", plateRecognitionReportMessage);
    }
}

4.6 发送延迟消息

要发送延迟消息,需要先开启emq延迟发布配置。

发送延时消息的方式相比之前,仅仅增加一个延时时间。其中延时时长的单位为,最大为4294967

//发送一个延时时长为10s的消息
defaultMQTTClient.publish2ThirdParty("npt/park/type1/dev/095437323930030130523933", 1, heartbeatReplyMessage, 10);

4.7 多数据源

多数据源与单数据源配置属性相同,在配置文件中声明即可

spring:
  mqtt:
    emq:
      client:
        # 多数据源客户端名称,默认default
        default:
          # broker地址
          host: 127.0.0.1
          # 端口
          port: 31883
          # 用户名
          username: admin
          # 密码
          password: 123456
        # 多数据源客户端名称
        parking:
          # broker地址
          host: 127.0.0.1
          # 端口
          port: 31883
          # 用户名
          username: admin
          # 密码
          password: 123456

4.7.1 发布消息

首先注入MQTTClient,与单数据源的唯一区别就是bean的名称。默认向Spring容器中添加的实现类名称为“${数据源名称}MQTTClient”

以上面的配置文件为例,默认的bean名称为 defaultMQTTClientbillMQTTClient

import com.demo.mqttclient.MQTTClient;
@Resource
private MQTTClient defaultMQTTClient;
@Resource
private MQTTClient billMQTTClient;

其他操作同单数据源

4.7.2 接收消息

接收消息与单数据源基本一致,唯一的区别是在@MQTTSubscriber中指定clientName属性,指定当前从哪个数据源进行消费。

import com.demo.mqttclient.anno.MQTTSubscriber;
@MQTTSubscriber(topics = {"npt/park/type1/server/10010"}, clientName = "parking")
public class ParkingMessageHandler {}

4.8 分组共享订阅

系统默认使用spring.application.name作为分组名称,用户可在消息消费类上指定@MQTTSubscriber属性中groupName = "group_name"即可

import com.demo.mqttclient.anno.MQTTSubscriber;
@MQTTSubscriber(topics = ParkingMessageHandler.TOPIC, qos = 1, share = ShareModelEnum.GROUP_SHARE, groupName = "group_name")
public class ParkingMessageHandler {}

4.9 不分组共享订阅

只需要在消息消费类上指定@MQTTSubscriber属性中share = ShareModelEnum.UN_GROUP_SHARE即可。

import com.demo.mqttclient.anno.MQTTSubscriber;
@MQTTSubscriber(topics = ParkingMessageHandler.TOPIC, qos = 1, share = ShareModelEnum.UN_GROUP_SHARE)
public class ParkingMessageHandler {}

4.10 排它订阅

只需要在消息消费类上指定@MQTTSubscriber属性中exclusive = true即可,开启排它订阅时,默认关闭共享订阅。

import com.demo.mqttclient.anno.MQTTSubscriber;
@MQTTSubscriber(topics = ParkingMessageHandler.TOPIC, qos = 1, exclusive = true)
public class ParkingMessageHandler {}
相关实践学习
钉钉群中如何接收IoT温控器数据告警通知
本实验主要介绍如何将温控器设备以MQTT协议接入IoT物联网平台,通过云产品流转到函数计算FC,调用钉钉群机器人API,实时推送温湿度消息到钉钉群。
阿里云AIoT物联网开发实战
本课程将由物联网专家带你熟悉阿里云AIoT物联网领域全套云产品,7天轻松搭建基于Arduino的端到端物联网场景应用。 开始学习前,请先开通下方两个云产品,让学习更流畅: IoT物联网平台:https://iot.console.aliyun.com/ LinkWAN物联网络管理平台:https://linkwan.console.aliyun.com/service-open
目录
相关文章
|
5月前
|
消息中间件 Java C语言
消息队列 MQ使用问题之在使用C++客户端和GBase的ESQL进行编译时出现core dump,该怎么办
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
2月前
|
网络协议 物联网 网络性能优化
物联网协议比较 MQTT CoAP RESTful/HTTP XMPP
【10月更文挑战第18天】本文介绍了物联网领域中四种主要的通信协议:MQTT、CoAP、RESTful/HTTP和XMPP,分别从其特点、应用场景及优缺点进行了详细对比,并提供了简单的示例代码。适合开发者根据具体需求选择合适的协议。
61 5
|
3月前
|
网络协议 物联网 网络性能优化
物联网江湖风云变幻!MQTT CoAP RESTful/HTTP XMPP四大门派谁主沉浮?
【9月更文挑战第3天】物联网(IoT)的兴起催生了多种通信协议,如MQTT、CoAP、RESTful/HTTP和XMPP,各自适用于不同场景。本文将对比这些协议的特点、优缺点,并提供示例代码。MQTT轻量级且支持QoS,适合大规模部署;CoAP基于UDP,适用于低功耗网络;RESTful/HTTP易于集成但不适合资源受限设备;XMPP支持双向通信,适合复杂交互应用。通过本文,开发者可更好地选择合适的物联网通信协议。
42 2
|
4月前
|
网络协议 物联网 网络性能优化
物联网江湖风云变幻!MQTT CoAP RESTful/HTTP XMPP四大门派谁主沉浮?
【8月更文挑战第14天】本文概览了MQTT、CoAP、RESTful/HTTP及XMPP四种物联网通信协议。MQTT采用发布/订阅模式,轻量高效;CoAP针对资源受限设备,基于UDP,低延迟;RESTful/HTTP易于集成现有Web基础设施;XMPP支持双向通信,扩展性强。每种协议均附有示例代码,助您根据不同场景和设备特性作出最佳选择。
43 5
|
4月前
|
物联网 C# 智能硬件
智能家居新篇章:WPF与物联网的智慧碰撞——通过MQTT协议连接与控制智能设备,打造现代科技生活的完美体验
【8月更文挑战第31天】物联网(IoT)技术的发展使智能家居设备成为现代家庭的一部分。通过物联网,家用电器和传感器可以互联互通,实现远程控制和状态监测等功能。本文将探讨如何在Windows Presentation Foundation(WPF)应用中集成物联网技术,通过具体示例代码展示其实现过程。文章首先介绍了MQTT协议及其在智能家居中的应用,并详细描述了使用Wi-Fi连接方式的原因。随后,通过安装Paho MQTT客户端库并创建MQTT客户端实例,演示了如何编写一个简单的WPF应用程序来控制智能灯泡。
135 0
|
4月前
|
物联网 网络性能优化 Python
"掌握MQTT协议,开启物联网通信新篇章——揭秘轻量级消息传输背后的力量!"
【8月更文挑战第21天】MQTT是一种轻量级的消息传输协议,以其低功耗、低带宽的特点在物联网和移动应用领域广泛应用。基于发布/订阅模型,MQTT支持三种服务质量级别,非常适合受限网络环境。本文详细阐述了MQTT的工作原理及特点,并提供了使用Python `paho-mqtt`库实现的发布与订阅示例代码,帮助读者快速掌握MQTT的应用技巧。
92 0
|
5月前
|
消息中间件 JavaScript Linux
消息队列 MQ操作报错合集之客户端在启动时遇到了连接错误,是什么原因
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
5月前
|
消息中间件 存储 负载均衡
消息队列 MQ使用问题之如何在grpc客户端中设置负载均衡器
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
5月前
|
消息中间件 小程序 RocketMQ
消息队列 MQ使用问题之如何在小程序中引用paho-mqtt
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
2月前
|
消息中间件 JSON Java
开发者如何使用轻量消息队列MNS
【10月更文挑战第19天】开发者如何使用轻量消息队列MNS
84 6

热门文章

最新文章

相关产品

  • 云消息队列 MQ