手把手带你搭建 MQTT服务(EMQX)
前言
欢迎来到 xxxzsh 的 MQTT 三部曲。
这是一个围绕 MQTT 的系列文章,我会用三篇内容,带你完整走一遍 MQTT 在真实项目中的使用过程——从理解协议,到搭建服务,再到 Java 项目接入。
在上一篇里,我们已经把 MQTT 的基本概念、设计思路以及它适合解决什么问题讲清楚了。这一篇我们直接进入实战:
搭建一个 MQTT Broker,本篇会以 EMQX 作为示例,带大家完成:
MQTT 服务的启动
管理控制台的使用
客户端连接与账号配置
基础的权限隔离思路
本文不会再重复 MQTT 的基础原理,因此更适合已经对 MQTT 有基本了解的同学阅读。
如果你想先系统了解 MQTT,可以先阅读第一篇:👉 MQTT 是什么?一文读懂 MQTT 协议的原理与优势(点击跳转)
一、MQTT ≠ 服务
学习了MQTT原理的小伙伴应该明白,但由于这个概念非常重要,所以我还是要重申一下。
在实际使用中,我们需要部署一个实现了 MQTT 协议的 消息服务器(Broker),客户端才能通过 MQTT 协议进行通信。
常见的 MQTT Broker 实现包括 Mosquitto、HiveMQ、EMQX 等。
本文选择 EMQX 作为 MQTT Broker,并通过 Docker 的方式进行部署。后文中提到的“MQTT 服务”,均指 EMQX 提供的 MQTT Broker 能力,而非 MQTT 协议本身。
下面开始运行EMQX:
二、Docker运行EMQX
创建挂载目录
mkdir -p /opt/emqx/{data,log,etc}
chmod -R 777 /opt/emqx
docker 启动:
docker run -d \
--name emqx \
--restart unless-stopped \
-p 1883:1883 \
-p 8883:8883 \
-p 8083:8083 \
-p 8084:8084 \
-p 18083:18083 \
-v /opt/emqx/data:/opt/emqx/data \
-v /opt/emqx/log:/opt/emqx/log \
emqx/emqx:5.8.8
docker-compose 启动:
emqx:
image: emqx:5.8.8
container_name: emqx
restart: unless-stopped
environment:
- EMQX_NODE__NAME=emqx@127.0.0.1 # 固定节点名称
ports:
- "1883:1883" # MQTT TCP
- "8883:8883" # MQTT SSL
- "8083:8083" # MQTT over WebSocket
- "8084:8084" # MQTT over WSS
- "18083:18083" # EMQX Dashboardvolumes:
- /opt/emqx/data:/opt/emqx/data
- /opt/emqx/log:/opt/emqx/log
端口说明
| 端口 | 作用 |
|---|---|
| 1883 | MQTT TCP(重点) |
| 8883 | MQTT SSL |
| 8083 | MQTT over WS |
| 8084 | MQTT over WSS |
| 18083 | 管理后台(重点) |
最主要的就是这个1883了,未来最基本的TCP连接就是用这个1883。
看看日志:
docker logs -f emqx

这样就是OK了。
三、使用EMQX管理控制台
浏览器访问
你的IP:18083
账号密码默认是 admin \ public
第一次登录后要改密,记得保存。

后续我们就要通过这个控制台,去配置EMQX的客户端连接、ACL等。

四、使用 MQTT 客户端工具验证 EMQX 服务
前面我们已经通过 Docker 成功部署了 EMQX(MQTT Broker)。
接下来,在正式开发客户端之前,通常需要先验证 Broker 是否能够正常连接和收发消息。
类似于:
-
MySQL 常用 Navicat
-
Redis 常用 Redis Desktop Manager(RDM)
4.1 常见的 MQTT 客户端工具
目前常用的 MQTT 客户端工具包括:
-
MQTTX
-
MQTT Explorer
-
mosquitto_pub / mosquitto_sub(命令行)
感觉各有各的好处吧。本文以 MQTTX 为例进行演示。官网地址:
MQTTX:全功能 MQTT 客户端工具
https://mqttx.app/zh你可以用在线的,也可以下载个桌面版。(MQTTX和EMQX是一家公司做的,所以你会发现整体UI风格很像):

4.2 客户端工具连接
打开MQTTX,新建连接:

Client ID 得是全局唯一的。
账号密码不用填,因为现在是默认开放状态。尝试一下连接,并复制一个连接:

连接之后,我们可以新建一个监听:

然后我们现在来模拟一下收发。开两个窗口:

到这里就是OK了。你已经成功搭建好了EMQX(MQTT Broker)。如果是基本的学习等,现在我们已经可以开始用了。比如接入你的Java项目、大数据平台等等。
五、客户端认证
上面我们已经搭建好了 EMQX,但实际使用中,我们肯定是配置访问账号密码,不可能公网暴漏出去给别人随便访问。
我们现在就来创建一下账号密码。
EMQX提供了非常方便的创建认证方式,打开管理控制台。
5.1 创建数据源
左侧 访问控制 -> 客户端认证 -> 点击右上角的创建。
可以看到它有很多选择,内置数据库、MySQL、MongoDB等常用数据源都可以。我们就用内置数据库就好,参数配置默认。
注意:
一旦启用了认证方式,EMQX 会默认拒绝所有匿名连接,只有通过认证的客户端才允许建立连接。所以如果你的MQTT正在被一些项目使用,请注意项目情况。

这个时候你再去客户端工具连接你就发现连不上了:

5.2 新建用户
我创建了一个账号test,密码testtest的用户:

打开连接工具,输入账号密码就发现可以连接了:

六、客户端授权(权限隔离)
在上一节中,我们已经通过 客户端认证 实现了「只有知道账号密码的客户端才能连接 EMQX」。
但在生产中这还远远不够。
6.1 为什么需要客户端授权(ACL)
我们先举一个非常典型的业务场景:
-
系统中有很多设备
-
每个设备都有自己的 Topic:
-
device/{deviceId}/up
device/{deviceId}/down -
不同客户端的权限应该不同:
-
普通用户:只能操作自己的设备
-
管理端:可以查看所有设备
-
第三方系统:只读,不能发指令
-
如果没有权限隔离,会发生什么?
-
任意客户端都能:
-
订阅所有设备数据
-
向任意设备下发指令
-
-
一旦账号泄露,后果不可控
因此,客户端授权(ACL)是 MQTT 系统中非常核心的一环。
6.2 什么是 ACL(Access Control List)
ACL,全称 Access Control List(访问控制列表)。
在 MQTT 中,ACL 本质上解决的是一个问题:
谁(客户端)
能不能
对某个 Topic
进行发布(publish)或订阅(subscribe)
可以简单理解为一个三元组:
Client + Action + Topic
其中:
-
Client:客户端身份(username / clientId 等)
-
Action:
-
publish(发布)
-
subscribe(订阅)
-
-
Topic:MQTT 的主题路径
6.3 ACL 规则基本语法(不需要记)
EMQX 中,File 的一条 ACL 规则的基本格式如下:
allow | deny who action topic
拆解说明:
| 部分 | 含义 |
|---|---|
| allow / deny | 允许 / 拒绝 |
| who | username / clientid / all |
| action | publish / subscribe / pubsub |
| topic | 具体的 Topic(支持通配符) |
打开 EMQX 控制台,打开左侧 访问控制 -> 客户端授权 ,可以看到默认的时候,就能看到EMQX已经配置了一个File了:
打开可以看到:

按上面的语法依次解释就是:
- 允许 EMQX 管理后台(dashboard 用户)订阅系统监控 Topic
- 来自本机的客户端,对所有 Topic 可随便收、随便发
- 禁止任何客户端通过通配符一次性订阅整个 MQTT 系统
- 如果前面都没匹配到,那就默认允许一切行为
看到3,这个就是我们不能订阅 # 来监听全部topic的原因:

我们注释掉,更新,再去连接就会发现可以订阅 # 了。

再举几个简单的例子:
允许所有客户端订阅所有设备的上报 Topic:
allow all subscribe device/+/up
禁止任何客户端向任何 Topic 发布消息:
deny all publish #
6.4 EMQX 内置数据库配置ACL
和客户端认证一样,除了文件,我们还可以配置其它数据源。
同样打开 EMQX 控制台中的客户端授权,点击右上角的创建,本文后续的演示中我们还是使用内置数据库,如下:

注意!
多个数据源存在的话,它就是匹配多个规则,哪个能匹配上就哪个。比如说,
- 全局的File里面我们配了禁止 # 订阅全部
- 在内置数据源里面配置了 zhangsan 这个号可以订阅 #
那么,zhangsan也可以订阅成功 # 。
6.5 示例场景
下面我们通过一个非常贴近真实业务的例子,来演示 ACL 的配置方式。
假设我们有两个用户:
用户一:张三(username = zhangsan)
-
只允许:
-
向自己设备
S001上报数据 -
接收自己设备的消息
-
-
不能访问任何其它设备
设备 Topic 设计如下:
device/S001/up
device/S001/down
内置数据库的配置就方便的多了,EMQX已经给我们封装好了。如下就是配置了zhangsan这个用户的:

用户二:李四(username = lisi)
-
只允许:
-
接收所有设备的上报数据
-
-
不允许:
-
向任何 Topic 发布消息
-

6.6 在 EMQX 里,ACL 能做到什么程度?
| 限制 publish / subscribe | ✅ |
| 精确 Topic | ✅ |
| 通配符(+ / #) | ✅ |
| 基于 username | ✅ |
| 基于 clientId | ✅ |
| 基于 IP | ✅ |
| 动态规则 | ✅ |
| 数据库 / HTTP / JWT | ✅ |
它支持动态占位符。比如 ${username} clientId之类的。
6.7 优化Topic设计
从上面的 6.5 我们了解到了每个用户都可以配置自己的ACL,但这时你会发现这样一个个
device/设备ID/up(down)
之类的配置过去,非常麻烦,总不能以后加一个设备来编辑一次吧。
所以我们可以试着优化一下MQTT的Topic的设计。
首先,我们明确一下需求:
1,除了用户自己用户名下的topic,其它topic都不能发布和订阅
2,配置完后续不需要任何的改动
第一点我们直接配置一个所有用户拒绝 # 就好了。

第二点我们则需要设计一下。
我给两个方案:
方案一
我们可以把设备上报的Topic改成:
device/所属用户/up
举个例子,zhangsan的两台设备S001和S002原先是上报到
device/S001/up
device/S002/up
那么我们都统一到:
device/zhangsan/up
然后在上报的数据中,去定义比如
{
"id": "S001",
"status": "active"...
}
这样后续不管来多少设备,都是往用户的固定的topic发,就不需要每次来都配置ACL了。
这样我们的EMQX中,就可以配置一个全局规则(所有用户):

device/${username}/up
需要注意的是,拒绝 # 一定是要在最底下,不然连接会直接被拒绝。
方案二
其实也类似,也是先用username包一层,只不过后面跟着设备ID区分。
还是以zhangsan的两台设备S001和S002的两台为例:
device/zhangsan/S001/up
device/zhangsan/S002/up
...
然后在所有用户配置中就可以用
device/${username}/#
或者
device/${username}/+/up
设计起来反正都大差不差。我这里就抛砖引玉一下,大家在实战中摸索两次就懂了。
七、总结
本文中,我们完成了从 EMQX 环境搭建 到 生产级权限管控 的全过程:
-
快速部署:利用 Docker 的便捷性,我们几分钟内就搭建起了一个高性能的 MQTT Broker。
-
安全加固:通过“客户端认证”,确保只有合法用户能进入系统。
-
精细化管理:通过“ACL 授权”,我们实现了业务上的权限隔离,彻底解决了设备间数据误发、越权监听的安全隐患。
-
架构优化:在最后的 Topic 设计优化中,我们看到了如何利用 动态占位符 实现“一劳永逸”的权限规则配置。
其实关键就在于对安全和规范的把控。MQTT 协议虽然简洁,但配合 EMQX 强大的认证授权机制,才能真正支撑起海量设备的安全通信。
希望这篇文章能帮你打通 MQTT 学习思路。
更多推荐




所有评论(0)