别再只用 WebSocket 做即时通讯了!MQTT+RabbitMQ 实战教程,轻量又高效

在日常开发中,即时通讯功能并不少见 ------ 电商平台的客服对话、支付完成后的实时通知、设备状态的动态同步,这些场景都需要快速、可靠的消息传递。过去我们常依赖 WebSocket,但最近发现,用 RabbitMQ 结合 MQTT 协议实现即时通讯,不仅代码量更少,带宽占用也更低,甚至无需编写后端代码就能满足基础需求。今天就带大家从零开始,掌握这套轻量级解决方案。

一、先搞懂:MQTT 是什么?核心概念有哪些?

要用好 MQTT,得先理清它的核心逻辑。MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议)是基于发布 / 订阅模式的轻量级协议,底层依赖 TCP/IP,最大优势是 "低开销、高可靠"------ 即使用有限带宽,也能为远程设备提供实时消息服务,特别适合物联网、即时通讯等场景。

它的核心组件只有 5 个,理解后就能轻松上手:

  • Publisher(发布者) :消息的发起者,负责向指定 "主题" 发送消息(比如用户发送的客服消息)。

  • Subscriber(订阅者) :消息的接收者,需先订阅某个 "主题",才能收到该主题下的所有消息(比如客服端接收用户消息)。

  • Broker(代理) :连接发布者和订阅者的 "中间件",负责转发消息,支持 MQTT 协议的消息中间件(如 RabbitMQ、EMQX)都能充当 Broker。

  • Topic(主题) :消息的 "路由标识",类似 "频道",发布者向某个 Topic 发消息,只有订阅了该 Topic 的订阅者能收到(比如 "user/123/msg" 代表用户 123 的消息主题)。

  • QoS(消息质量) :控制消息传递的可靠性,分三个等级,按需选择即可:

    • QoS 0(至多一次) :只发送一次,不保证送达,可能丢失或重复(适合非关键消息,如设备心跳)。
    • QoS 1(至少一次) :确保消息送达,但可能重复(适合重要消息,如通知提醒)。
    • QoS 2(恰好一次) :确保消息仅送达一次,无丢失无重复(适合核心业务,如支付通知)。

二、第一步:启用 RabbitMQ 的 MQTT 功能

RabbitMQ 默认不开启 MQTT 支持,需要手动配置。这里以 Docker 环境为例,步骤简单且跨平台,几分钟就能搞定。

1. 用 Docker 启动 RabbitMQ

首先运行 Docker 命令,拉取并启动 RabbitMQ 容器,同时映射必要端口(1883 是 MQTT 默认端口,15675 是 MQTT WebSocket 端口,15672 是管理控制台端口):

bash

bash 复制代码
docker run -p 5672:5672 -p 15672:15672 -p 1883:1883 -p 15675:15675 --name rabbitmq-mqtt \
-v /mydata/rabbitmq-mqtt/data:/var/lib/rabbitmq \
-d rabbitmq:3.9.11-management
  • 5672:RabbitMQ 默认 AMQP 协议端口(非必需,此处为兼容其他功能);
  • 15672:RabbitMQ 管理控制台端口(用于查看服务状态);
  • 1883:MQTT 协议默认端口(用于 TCP 连接);
  • 15675:MQTT WebSocket 端口(用于前端 / 客户端通过 WebSocket 连接)。

2. 启用 MQTT 插件

容器启动后,需要进入容器内部开启 MQTT 相关插件(开启rabbitmq_web_mqtt会自动关联启用rabbitmq_mqtt):

bash

bash 复制代码
# 进入RabbitMQ容器
docker exec -it rabbitmq-mqtt /bin/bash
# 启用MQTT Web插件
rabbitmq-plugins enable rabbitmq_web_mqtt

3. 验证配置是否成功

打开浏览器访问 RabbitMQ 管理控制台(地址:http://你的服务器IP:15672,默认账号密码都是guest),在 "Listening ports" 列表中,若能看到http/web-mqtt对应 15675 端口、mqtt对应 1883 端口,说明配置成功。

三、用 MQTTX 客户端测试:快速验证即时通讯

在写代码前,先用 MQTTX(一款轻量的 MQTT 客户端工具)测试 RabbitMQ 的 MQTT 功能是否正常,避免后续踩坑。

1. 用 Docker 启动 MQTTX

同样用 Docker 快速部署 MQTTX Web 版,命令如下:

bash

arduino 复制代码
docker run -p 80:80 --name mqttx-web -d emqx/mqttx-web

启动后访问http://你的服务器IP,即可进入 MQTTX 控制台。

2. 创建 MQTT 连接并测试

  1. 点击左侧 "+" 号新建连接,配置关键参数:

    • 名称:自定义(如 "test-connect");
    • 服务器地址:ws://你的服务器IP:15675/ws(注意用 WebSocket 协议,路径为/ws);
    • 端口:15675;
    • 用户名 / 密码:guest/guest
    • MQTT 版本:选择 3.1.1(兼容性更好)。
  2. 连接成功后,添加订阅主题(如 "testTopicA"),QoS 选择 0 或 1;

  3. 再打开一个 MQTTX 窗口,同样连接 RabbitMQ,向 "testTopicA" 主题发送消息(如 "hello MQTT!");

  4. 此时第一个窗口会实时收到消息,说明 RabbitMQ 的 MQTT 转发功能正常。

四、前端零后端代码实现:HTML+JS 打造简单聊天

验证功能正常后,直接用前端技术(HTML+JavaScript)实现即时通讯 ------ 无需写一行后端代码,只需引入MQTT.js库即可。

1. 核心代码实现

创建index.html文件,核心逻辑是 "连接 MQTT 服务→订阅主题→发送 / 接收消息":

html

xml 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>MQTT前端聊天Demo</title>
    <style>
        .message-box { margin-top: 10px; padding: 10px; border: 1px solid #eee; height: 300px; overflow-y: auto; }
        .message-item { margin: 5px 0; padding: 8px; background: #f5f5f5; }
    </style>
</head>
<body>
    <div>
        <label>订阅主题:<span id="subscribedTopic"></span></label><br>
        <label>目标主题:<input id="targetTopic" type="text" placeholder="输入要发送的主题" value="testTopicB"></label><br>
        <label>消息内容:<input id="msgContent" type="text" placeholder="输入消息内容"></label><br>
        <button onclick="sendMsg()">发送消息</button>
        <button onclick="clearMsg()">清空记录</button>
        <div class="message-box" id="messageBox"></div>
    </div>

    <!-- 引入MQTT.js库 -->
    <script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
    <script>
        // 1. 配置MQTT连接参数
        const mqttUrl = 'ws://你的服务器IP:15675/ws'; // RabbitMQ的MQTT WebSocket地址
        const subscribedTopic = getUrlParam('topic') || 'testTopicA'; // 从URL获取订阅主题,默认testTopicA
        document.getElementById('subscribedTopic').textContent = subscribedTopic;

        // 2. 连接MQTT服务
        const client = mqtt.connect(mqttUrl, {
            username: 'guest',
            password: 'guest',
            keepalive: 60 // 心跳间隔(秒)
        });

        // 3. 连接成功后订阅主题
        client.on('connect', () => {
            client.subscribe(subscribedTopic, (err) => {
                if (!err) {
                    addMessage(`✅ 成功订阅主题:${subscribedTopic}`);
                } else {
                    addMessage(`❌ 订阅失败:${err.message}`);
                }
            });
        });

        // 4. 接收消息并展示
        client.on('message', (topic, msg) => {
            addMessage(`📥 收到[${topic}]的消息:${msg.toString()}`);
        });

        // 5. 发送消息到指定主题
        function sendMsg() {
            const targetTopic = document.getElementById('targetTopic').value;
            const content = document.getElementById('msgContent').value;
            if (!targetTopic || !content) {
                alert('请填写目标主题和消息内容!');
                return;
            }
            // 发布消息
            client.publish(targetTopic, content);
            addMessage(`📤 发送到[${targetTopic}]的消息:${content}`);
            document.getElementById('msgContent').value = ''; // 清空输入框
        }

        // 6. 工具函数:添加消息到页面
        function addMessage(text) {
            const msgBox = document.getElementById('messageBox');
            const msgItem = document.createElement('div');
            msgItem.className = 'message-item';
            msgItem.textContent = `[${new Date().toLocaleTimeString()}] ${text}`;
            msgBox.appendChild(msgItem);
            // 滚动到最新消息
            msgBox.scrollTop = msgBox.scrollHeight;
        }

        // 7. 工具函数:清空消息记录
        function clearMsg() {
            document.getElementById('messageBox').innerHTML = '';
        }

        // 8. 工具函数:从URL获取参数
        function getUrlParam(name) {
            const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i');
            const match = window.location.search.substr(1).match(reg);
            return match ? decodeURIComponent(match[2]) : null;
        }
    </script>
</body>
</html>

2. 测试前端聊天功能

  1. index.html放在静态资源目录(如 Nginx 或 SpringBoot 的resources/static);

  2. 打开两个浏览器窗口,分别访问:

    • 窗口 1(订阅 testTopicA):http://你的地址/index.html?topic=testTopicA
    • 窗口 2(订阅 testTopicB):http://你的地址/index.html?topic=testTopicB
  3. 在窗口 1 的 "目标主题" 输入testTopicB,发送消息;窗口 2 会实时收到,反之亦然 ------ 一个简单的跨窗口聊天功能就实现了!

五、SpringBoot 集成 MQTT:应对复杂业务场景

如果需要后端参与消息处理(如消息存储、权限校验、多主题转发),可以将 MQTT 集成到 SpringBoot 中。下面是完整的实现步骤。

1. 添加依赖

pom.xml中引入 Spring 集成 MQTT 的依赖:

xml

xml 复制代码
<!-- Spring Integration MQTT -->
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-mqtt</artifactId>
</dependency>
<!-- 可选:Swagger用于接口测试 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>

2. 配置 MQTT 参数

application.yml中添加 RabbitMQ 的 MQTT 连接信息:

yaml

yaml 复制代码
rabbitmq:
  mqtt:
    url: tcp://你的服务器IP:1883 # MQTT TCP协议地址
    username: guest
    password: guest
    default-topic: testTopic # 默认订阅/发布的主题
server:
  port: 8088 # 应用端口

3. 编写配置类

创建MqttConfig类,读取配置文件中的参数:

java

arduino 复制代码
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "rabbitmq.mqtt")
public class MqttConfig {
    /** MQTT连接地址(TCP协议) */
    private String url;
    /** 连接用户名 */
    private String username;
    /** 连接密码 */
    private String password;
    /** 默认主题 */
    private String defaultTopic;
}

4. 配置消息订阅者(接收消息)

创建MqttInboundConfig,实现 "订阅 MQTT 主题并处理消息" 的逻辑:

java

java 复制代码
import lombok.extern.slf4j.Slf4j;
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.MessageHandler;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
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.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessagingException;

@Slf4j
@Configuration
public class MqttInboundConfig {

    @Autowired
    private MqttConfig mqttConfig;

    /** 定义输入通道:用于接收MQTT消息 */
    @Bean
    public MessageChannel mqttInputChannel() {
        return new DirectChannel();
    }

    /** 定义消息生产者:订阅指定主题,将消息转发到输入通道 */
    @Bean
    public MqttPahoMessageDrivenChannelAdapter inbound() {
        // 参数:MQTT连接地址、客户端ID(唯一)、订阅的主题
        MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter(
                mqttConfig.getUrl(), "springboot-mqtt-subscriber", mqttConfig.getDefaultTopic()
        );
        adapter.setCompletionTimeout(5000); // 连接超时时间
        adapter.setConverter(new DefaultPahoMessageConverter()); // 消息转换器
        adapter.setQos(1); // 消息质量等级(至少一次)
        adapter.setOutputChannel(mqttInputChannel()); // 绑定输入通道
        return adapter;
    }

    /** 定义消息处理器:处理输入通道中的消息 */
    @Bean
    @ServiceActivator(inputChannel = "mqttInputChannel")
    public MessageHandler mqttMessageHandler() {
        return new MessageHandler() {
            @Override
            public void handleMessage(Message<?> message) throws MessagingException {
                // 此处可添加业务逻辑:如消息存储、格式转换、权限校验等
                log.info("📥 收到MQTT消息:{}", message.getPayload());
            }
        };
    }

    /** MQTT客户端工厂:配置连接参数 */
    @Bean
    public MqttPahoClientFactory mqttClientFactory() {
        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
        org.eclipse.paho.client.mqttv3.MqttConnectOptions options = new org.eclipse.paho.client.mqttv3.MqttConnectOptions();
        options.setServerURIs(new String[]{mqttConfig.getUrl()});
        options.setUserName(mqttConfig.getUsername());
        options.setPassword(mqttConfig.getPassword().toCharArray());
        factory.setConnectionOptions(options);
        return factory;
    }
}

5. 配置消息发布者(发送消息)

创建MqttOutboundConfig,实现 "从后端发送消息到 MQTT 主题" 的逻辑:

java

kotlin 复制代码
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;

@Configuration
public class MqttOutboundConfig {

    @Autowired
    private MqttConfig mqttConfig;

    @Autowired
    private MqttPahoClientFactory mqttClientFactory;

    /** 定义输出通道:用于发送MQTT消息 */
    @Bean
    public MessageChannel mqttOutboundChannel() {
        return new DirectChannel();
    }

    /** 定义消息处理器:将输出通道的消息发布到MQTT主题 */
    @Bean
    @ServiceActivator(inputChannel = "mqttOutboundChannel")
    public MqttPahoMessageHandler mqttOutbound() {
        // 参数:客户端ID(唯一)、客户端工厂
        MqttPahoMessageHandler handler = new MqttPahoMessageHandler("springboot-mqtt-publisher", mqttClientFactory);
        handler.setAsync(true); // 异步发送
        handler.setDefaultTopic(mqttConfig.getDefaultTopic()); // 默认发布主题
        return handler;
    }
}

6. 定义 MQTT 网关(简化发送逻辑)

创建MqttGateway接口,通过注解简化 "发送消息到 MQTT" 的调用:

java

less 复制代码
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.handler.annotation.Header;

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

    /**
     * 发送消息到默认主题
     * @param payload 消息内容
     */
    void sendToDefaultTopic(String payload);

    /**
     * 发送消息到指定主题
     * @param payload 消息内容
     * @param topic 目标主题
     */
    void sendToTopic(String payload, @Header(MqttHeaders.TOPIC) String topic);

    /**
     * 发送消息到指定主题,并设置QoS
     * @param topic 目标主题
     * @param qos 消息质量等级
     * @param payload 消息内容
     */
    void sendToTopicWithQos(@Header(MqttHeaders.TOPIC) String topic, 
                           @Header(MqttHeaders.QOS) int qos, 
                           String payload);
}

7. 编写测试接口

创建MqttController,提供 HTTP 接口测试 MQTT 消息发送:

java

less 复制代码
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequestMapping("/mqtt")
@Tag(name = "MqttController", description = "MQTT消息测试接口")
public class MqttController {

    @Autowired
    private MqttGateway mqttGateway;

    @PostMapping("/send/default")
    @Operation(summary = "向默认主题发送消息")
    public String sendToDefault(@RequestParam String payload) {
        mqttGateway.sendToDefaultTopic(payload);
        return "消息发送成功:" + payload;
    }

    @PostMapping("/send/custom")
    @Operation(summary = "向自定义主题发送消息")
    public String sendToCustom(@RequestParam String payload, 
                               @RequestParam String topic) {
        mqttGateway.sendToTopic(payload, topic);
        return "向主题[" + topic + "]发送消息成功:" + payload;
    }
}

8. 测试后端集成功能

  1. 启动 SpringBoot 应用,访问 Swagger 地址:http://localhost:8088/swagger-ui.html
  2. 调用/mqtt/send/custom接口,传入payload="来自后端的消息"topic="testTopicA"
  3. 查看应用日志,会打印 "收到 MQTT 消息:来自后端的消息",同时订阅了testTopicA的前端窗口也会实时收到消息,说明集成成功。

六、总结:MQTT+RabbitMQ 的优势与适用场景

相比 WebSocket,MQTT+RabbitMQ 的组合有 3 个明显优势:

  1. 轻量高效:MQTT 协议头部仅 2 字节,带宽占用远低于 WebSocket,适合低带宽场景(如物联网设备)。

  2. 无需手写后端:基础即时通讯场景(如简单聊天、通知)可直接用前端 + RabbitMQ 实现,无需开发后端服务。

  3. 可靠性高:依赖 RabbitMQ 的消息持久化、重试机制,配合 QoS 等级,能避免消息丢失,适合关键业务。

适用场景推荐:

  • 简单即时通讯:客服聊天、跨端通知;

  • 物联网场景:设备状态同步、传感器数据上报;

  • 轻量消息传递:支付通知、订单状态更新。

最后,附上本文实战项目的参考源码(基于 SpringBoot3,含前端 Demo):
github.com/macrozheng/...(该项目是标星 60K + 的电商实战项目,还包含微服务、Docker 部署等功能,值得学习)。

掌握这套方案,下次遇到即时通讯需求,就不用再局限于 WebSocket 啦[))](url)

相关推荐
P7Dreamer5 小时前
Vue 表格悬停复制指令:优雅地一键复制单元格内容
前端·vue.js
我的写法有点潮5 小时前
前端必须会的 TypedArray:一文吃透
前端·javascript
Mintopia5 小时前
扩散模型在 Web 图像生成中的技术演进:从“随机噪声”到“浏览器里的画家”
前端·javascript·aigc
跟橙姐学代码5 小时前
Python学习笔记:正则表达式一文通——从入门到精通
前端·python·ipython
召摇5 小时前
简洁语法的逻辑赋值操作符
前端·javascript
Watermelo6175 小时前
复杂计算任务的智能轮询优化实战
大数据·前端·javascript·性能优化·数据分析·云计算·用户体验
龙在天5 小时前
上线还好好的,第二天凌晨白屏,微信全屏艾特我...
前端
芝士加5 小时前
月下载超2亿次的npm包又遭投毒,我学会了搭建私有 npm 仓库!
前端·javascript·开源
前端世界5 小时前
前端必看:为什么同一段 CSS 在不同浏览器显示不一样?附解决方案和实战代码
前端·css