MQTT:从核心概念到Spring Boot整合实战

1. MQTT简介

1.1MQTT简介

MQTT是一种基于 "发布订阅模式"的轻量级的消息传输协议!,是一种传统的客户端-服务器架构的替代方案,因为一般传统的客户端-服务器是客户端能够直接和服务器进行通信完成消息的传输。发布订阅模式会将发送消息的发布者publisher与接收消息的订阅者subscribers进行分离,publisher与subscribers 并不会直接通信,他们甚至都不清楚对方是否存在,他们之间的交流由第三方组件broker代理。在这里插入图片描述
MQTT的核心特性和概念可总结如下:

1.2MQTT特性

  1. 轻量级: 适用于处理能力、内存和能耗有限的物联网设备,因其低开销和小报文实现高效通信。

  2. 可靠性: 提供多种QoS等级、会话感知和持久连接,确保即使在高延迟或不稳定的网络中也能可靠传递消息。

  3. 安全通信: 通过TLS/SSL加密及用户名/密码认证或客户端证书,保障数据传输的机密性和访问控制。

  4. 双向通信: 发布-订阅模式支持设备间无缝双向通信,简化新设备集成并提高系统扩展性。

  5. 语言支持: 广泛支持多种编程语言(如PHP、Node.js、Python、Golang、Java等),实现跨平台和技术的无缝通信。

1.3MQTT核心概念

  • MQTT客户端: 任何运行MQTT客户端库的应用或设备,如即时通讯应用、传感器和测试工具。

  • MQTT Broker: 负责处理客户端请求(如连接、断开、订阅)及消息转发,支持大量连接和高消息吞吐量。

  • 主题: UTF-8编码字符串,用于消息路由和分类,类似URL路径结构(如chat/room/1sensor/10/temperature),自动创建,无需手动管理。

1.4 控制报文简介

报文是网络中交换与传输的数据最小单元,通俗来讲就是站点一次性要发送的数据块。它包含了将要发送的完整数据信息,其长短不一致,长度不限且可变。MQTT 客户端和服务端通过交换控制报文来完成它们的工作,比如订阅主题和发布消息,无论是什么类型的控制报文,它们都由固定报头、可变报头和有效载荷三个部分组成。
MQTT 目前定义了 15 种控制报文类型,按照功能进行分类,我们可以将这些报文分为连接、发布、订阅三个类别:
在这里插入图片描述

1.5 QoS简介

使用MQTT协议的设备大部分都是运行在网络受限的环境下,而只依靠底层的TCP传输协议,并不能完全保证消息的可靠到达。
MQTT提供了QoS机制,其核心是设计了多种消息交互机制来提供不同的服务质量,来满足用户在各种场景下对消息可靠性的要求。

MQTT 定义了三个 QoS 等级,分别为:

  • QoS 0,最多交付一次 -----> 可能丢失消息
    QoS 0 是最低的 QoS 等级。QoS 0 消息即发即弃,不需要等待确认,不需要存储和重传,因此对于接收方来说,永远都不需要担心收到重复的消息。

  • QoS 1,至少交付一次 -----> 可以保证收到消息,但消息可能重复
    为了保证消息到达,QoS 1 加入了应答与重传机制,发送方只有在收到接收方的 PUBACK 报文以后,才能认为消息投递成功,在此之前,发送方需要存储该 PUBLISH 报文以便下次重传。

  • QoS 2,只交付一次 -----> 可以保证消息既不丢失也不重复
    QoS 2 解决了 QoS 0、1 消息可能丢失或者重复的问题,但相应地,它也带来了最复杂的交互流程和最高的开销。每一次的 QoS 2 消息投递,都要求发送方与接收方进行至少两次请求/响应流程。

1.6主题详解

MQTT 主题通配符包含单层通配符 + 及多层通配符 #,主要用于客户端一次订阅多个主题

2. MQTT实战场景搭建

2.1 代理服务器:EMQX部署

部署教程:https://docs.emqx.com/zh/enterprise/latest/deploy/install-docker.html
拉取镜像

docker pull emqx/emqx-enterprise:5.8.1

运行容器

docker run -d --name emqx-enterprise -p 1883:1883 -p 8083:8083 -p 8084:8084 -p 8883:8883 -p 18083:18083 emqx/emqx-enterprise:5.8.1

常见端口介绍:

端口号说明
1883TCP端口
8083WebSocket端口
8084WebSocket Secure 端口
8883SSL/TLS 端口
18083Broker的Dashboard访问端口号

2.2 客户端:MQTTX软件使用

MQTTX 是EMQX开源的一款跨平台 MQTT 5.0 客户端工具,它支持 macOS, Linux 并且支持自定义脚本模拟测试、MQTT 消息格式转换、日志记录等多个功能。
官网地址:https://mqttx.app/zh
使用教程不在赘述。

2.3 整合springboot

基础配置搭建
<dependencies>
	
    <!-- spring boot项目web开发的起步依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

	<!-- spring boot项目集成消息中间件基础依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-integration</artifactId>
    </dependency>

	<!-- spring boot项目和mqtt客户端集成起步依赖 -->
    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-mqtt</artifactId>
        <version>5.4.3</version>
    </dependency>

    <!-- lombok依赖 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

    <!-- fastjson依赖 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.83</version>
    </dependency>

</dependencies>

启动类添加配置

@EnableConfigurationProperties(value = MqttConfigurationProperties.class)

添加属性配置

spring.mqtt.username=admin
spring.mqtt.password=jin15738121358
spring.mqtt.url=tcp://192.168.253.166:1883
spring.mqtt.subClientId=sub_client_id_123
spring.mqtt.subTopic=atguigu/iot/lamp/#
spring.mqtt.pubClientId=pub_client_id_123

创建实体类读取自定义配置

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@ConfigurationProperties(prefix = "spring.mqtt")
public class MqttConfigurationProperties {

    private String username;
    private String password;
    private String url;
    private String subClientId ;
    private String subTopic ;
    private String pubClientId ;

}

创建配置类配置链接工厂

import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;

@Configuration
public class MqttConfiguration {

    @Autowired
    private MqttConfigurationProperties mqttConfigurationProperties ;

    @Bean
    public MqttPahoClientFactory mqttClientFactory(){

        // 创建客户端工厂
        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();

        // 创建MqttConnectOptions对象
        MqttConnectOptions options = new MqttConnectOptions();
        options.setCleanSession(true);
        options.setUserName(mqttConfigurationProperties.getUsername());
        options.setPassword(mqttConfigurationProperties.getPassword().toCharArray());
        options.setServerURIs(new String[]{mqttConfigurationProperties.getUrl()});
        factory.setConnectionOptions(options);

        // 返回
        return factory;
    }

}
订阅主题获取消息

配置入站适配器

import org.springframework.beans.factory.annotation.Autowired;
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.MqttPahoClientFactory;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;

@Configuration
public class MqttInboundConfiguration {

    @Autowired
    private MqttConfigurationProperties mqttConfigurationProperties ;

    @Autowired
    private ReceiverMessageHandler receiverMessageHandler;

    /**
     * 配置消息传输通道
     * @return
     */
    @Bean
    public MessageChannel mqttInputChannel() {
        return new DirectChannel();
    }

    /**
     * 配置入站适配器
     */
    @Bean
    public MessageProducer messageProducer(MqttPahoClientFactory mqttPahoClientFactory) {
        MqttPahoMessageDrivenChannelAdapter adapter  =
                new MqttPahoMessageDrivenChannelAdapter(mqttConfigurationProperties.getUrl() ,
                        mqttConfigurationProperties.getSubClientId() ,
                        mqttPahoClientFactory , mqttConfigurationProperties.getSubTopic().split(",")) ;
        adapter.setConverter(new DefaultPahoMessageConverter());
        adapter.setQos(1);
        adapter.setOutputChannel(mqttInputChannel());
        return adapter ;
    }

    /**
     * 配置入站消息处理器
     * @return
     */
    @Bean
    @ServiceActivator(inputChannel = "mqttInputChannel")
    public MessageHandler messageHandler() {
        return this.receiverMessageHandler ;
    }

}

定义监听主题消息的处理器

import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;
import org.springframework.stereotype.Component;

@Component
public class ReceiverMessageHandler implements MessageHandler {

    @Override
    public void handleMessage(Message<?> message) throws MessagingException {
        MessageHeaders headers = message.getHeaders();
        String receivedTopicName = (String) headers.get("mqtt_receivedTopic");
        System.out.println("receivedTopicName:"+receivedTopicName);
            System.out.println("接收到消息:" + message.getPayload());

    }

}

配置出站消息处理器

import org.springframework.beans.factory.annotation.Autowired;
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.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;

@Configuration
public class MqttOutboundConfiguration {

    @Autowired
    private MqttConfigurationProperties mqttConfigurationProperties ;

    @Autowired
    private MqttPahoClientFactory pahoClientFactory ;

    @Bean
    public MessageChannel mqttOutputChannel() {
        return new DirectChannel();
    }

    @Bean
    @ServiceActivator(inputChannel = "mqttOutputChannel")
    public MessageHandler mqttOutboundMassageHandler() {
        MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(mqttConfigurationProperties.getUrl() ,
                mqttConfigurationProperties.getPubClientId() , pahoClientFactory ) ;
        messageHandler.setAsync(true);
        messageHandler.setDefaultQos(0);
        messageHandler.setDefaultTopic("default");
        return messageHandler ;
    }

}

2、定义发送消息的网关接口

import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.handler.annotation.Header;

@MessagingGateway(defaultRequestChannel = "mqttOutputChannel")
public interface MqttGateway {

    /**
     * 发送mqtt消息
     * @param topic 主题
     * @param payload 内容
     */
    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String payload);

    /**
     * 发送包含qos的消息
     * @param topic 主题
     * @param qos 对消息处理的几种机制。
     *          * 0 表示的是订阅者没收到消息不会再次发送,消息会丢失。<br>
     *          * 1 表示的是会尝试重试,一直到接收到消息,但这种情况可能导致订阅者收到多次重复消息。<br>
     *          * 2 多了一次去重的动作,确保订阅者收到的消息有一次。
     * @param payload 消息体
     */
    void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload);
    
}

定义发送消息的服务类

import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@AllArgsConstructor
public class MqttMessageSender {

    private MqttGateway mqttGateway;

    /**
     * 发送mqtt消息
     * @param topic 主题
     * @param message 内容
     */
    public void send(String topic, String message) {
        mqttGateway.sendToMqtt(topic, message);
    }

    /**
     * 发送包含qos的消息
     * @param topic 主题
     * @param qos 质量
     * @param message 消息体
     */
    public void send(String topic, int qos, byte[] message){
        mqttGateway.sendToMqtt(topic, qos, Arrays.toString(message));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

kimloner

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

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

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

打赏作者

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

抵扣说明:

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

余额充值