赞
踩
本文上的常见问题是综合了从V4.0到V5.7的各个版本的常见问题解答。(完成日期:2024年8月4日)
主题使用没有数量限制,主题数量增长对性能影响不大,可以放心使用。
开源版不支持数据存储功能,可以使用企业版,或者使用外部程序订阅主题/Webhook 的方式获取数据,然后写入到数据库。
开源版不支持数据转储,此能力仅在企业版中提供。
来源:EMQX 与 NB-IoT、LoRAWAN 的关系是什么?
EMQX
是一个开源的MQTT 消息服务器,并且MQTT 是一个 TCP 协议栈上位于应用层的协议;NB-IoT
和 LoRAWAN
在 TCP
协议层处于物理层,负责物理信号的传输。因此两者在 TCP 协议栈的不同层次上,实现不同的功能。
来源:MQTT 协议与 HTTP 协议相比,有何优点和弱点?
HTTP 协议是一个无状态的协议,每个 HTTP 请求为 TCP 短连接,每次请求都需要重新创建一个 TCP 连接(可以通过 keep-alive 属性来优化 TCP 连接的使用,多个 HTTP 请求可以共享该 TCP 连接);而 MQTT 协议为长连接协议,每个客户端都会保持一个长连接。
MQTT 与 HTTP 协议相比优势在于:
mqueue 是 EMQX 在消息发布流程中保存在会话中的一个消息队列,当 MQTT 连接报文中的 clean session 设置为 false 的时候,即使是客户端断开连接的情况下,EMQX 也会为断连客户端保存一个会话,这个会话会缓存订阅关系,并代替断开连接的客户端去接收订阅主题上的消息,而这些消息会存在 EMQX 的 mqueue 中,等到客户端重新上线再将消息重新发给客户端。由于 qos0 消息在 MQTT 协议中的优先级比较低,所以 EMQX 默认不缓存 qos 0 的消息,mqueue 在 EMQX 中是可以配置的,通过配置 mqtt.mqueue_store_qos0 = true
可以将 qos0 消息也存在 mqueue 中,mqueue 的大小也是有限制的,通过配置项 mqtt.max_mqueue_len
,可以确定每个会话缓存的消息数量。
注意:这些消息是存储在内存中的,所以尽量不要将 mqueue 长度限制修改为 0(设置为 0 代表 mqueue 长度没有限制),否则在实际的业务场景中,有内存耗光的风险。
来源:什么是 WebSocket?什么情况下需要通过 WebSocket 去连接 EMQX 服务器?(按个人理解重新阐述)
WebSocket 是一种在基于 HTTP 协议上支持全双工通讯的协议,通过该协议,用户可以实现浏览器和服务器之间的双向通信,比如可以通过服务器往浏览器端推送消息。EMQX 提供了 WebSocket 连接支持,用户可以在浏览器端、微信/抖音小程序接入 EMQX,实现主题订阅、消息发布等操作。
高并发和高可用是 EMQX 的设计目标,为了实现这些目标 EMQX 中应用了多种技术,比如:
在精心设计和实现之后,单个 EMQX 节点就可以处理五百万连接。
EMQX 支持多节点集群,集群下整个系统的性能会成倍高于单节点,并能在单节点故障时保证系统服务不中断。
EMQX 会保证来自同一客户端的相同主题的消息按照到达顺序被转发,这与消息的 QoS 等级无关,QoS 等级不会影响转发顺序。不管消息丢失还是重复,也都不会导致消息失序。这也是 MQTT 协议所要求的。
但对于不同主题的消息,EMQX 不会提供转发顺序保证,我们可以将他们视为进入了不同的通道,比如主题 A 的消息先于主题 B 的消息到达 EMQX,但最终可能主题 B 的消息会更早被转发。
共享订阅是 MQTT 5.0 标准的新特性,在标准发布前,EMQX 就已经把共享订阅作为标准外特性进行了支持。在普通订阅中,所有订阅者都会收到订阅主题的所有消息,而在共享订阅中,订阅同一个主题的客户端会轮流的收到这个主题下的消息,也就是说同一个消息不会发送到多个订阅者,从而实现订阅端的多个节点之间的负载均衡。
共享订阅对于数据采集 / 集中处理类应用非常有用。在这样的场景下,数据的生产者远多余数据的消费者,且同一条数据只需要被任意消费者处理一次。
更多使用方式请参考共享订阅。
来源:什么是共享订阅?
共享订阅是 MQTT 5.0 引入的全新特性,它允许客户端以负载均衡的方式消费消息。我们可以将客户端划分为多个订阅组,消息仍然会被转发给所有订阅组,但一个订阅组内的客户端将以随机、轮询等策略交替接收消息。由于共享订阅的所有处理逻辑都在服务端完成,而客户端只需订阅 $share/group/{topic filter}
即可,所以 MQTT 3.1.1 的客户端也可以以相同方式使用共享订阅。
共享订阅在数据采集这类消息生产者远多于消费者的场景中非常有用,更多内容请参考 共享订阅。
来源:什么是离线消息?
一般情况下 MQTT 客户端仅在连接到消息服务器的时候,如果客户端离线将收不到消息。但是在客户端有固定的 ClientID,clean_session 为 false,且 QoS 设置满足服务器端的配置要求时,在客户端离线时,服务器可以为客户端保持一定量的离线消息,并在客户端再次连接时发送给客户端。
离线消息在网络连接不是很稳定时,或者对 QoS 有一定要求时非常有用。
通常情况客户端需要在连接到 EMQX 之后主动订阅主题。代理订阅是指服务器为客户端订阅主题,这一过程不需要客户端参与,客户端和需要代理订阅的主题的对应关系保存在服务器中。
使用代理订阅可以集中管理大量的客户端的订阅,同时为客户端省略掉订阅这个步骤,可以节省客户端侧的计算资源和网络带宽。
系统主题以 $SYS/
开头。EMQX 会以系统主题的方式周期性的发布关于自身运行状态、MQTT 协议统计、客户端上下线状态到系统主题。订阅系统主题可以获得这些信息。
这里列举一些系统主题,完整的系统主题请参考 EMQX 文档的相关章节:
$SYS/brokers
: 集群节点列表。$SYS/brokers/${node}/clients/${clientid}/connected
: 当客户端连接时发送的客户端信息。$SYS/broker/${node}/stats/connections/count
: 当前客户端总数。$SYS/broker/${node}/stats/sessions/count
: 当前会话总数。问题:
执行 ./bin/emqx console
输出的错误内容包含:
{application_start_failure,kernel,{{shutdown,{failed_to_start_child,kernel_safe_sup,{on_load_function_failed,crypto}}}, ..}
它表示,EMQX 依赖的 Erlang/OTP 中的 crypto
应用启动失败。
解决方法:
lib
目录同级的位置)## 安装包安装
cd emqx
## 包管理器安装,例如 yum。则它的 lib 目录应该在 /lib/emqx
cd /lib/emqx
查询 crypto
依赖的 .so
动态库列表及其在内存中的地址:
ldd lib/crypto-*/priv/lib/crypto.so
lib/crypto-4.6/priv/lib/crypto.so: /lib64/libcrypto.so.10: version `OPENSSL_1.1.1' not found (required by lib/crypto-4.6/priv/lib/crypto.so)
linux-vdso.so.1 => (0x00007fff67bfc000)
libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00007fee749ca000)
libc.so.6 => /lib64/libc.so.6 (0x00007fee74609000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fee74404000)
libz.so.1 => /lib64/libz.so.1 (0x00007fee741ee000)
/lib64/ld-linux-x86-64.so.2 (0x00007fee74fe5000)
其中 OPENSSL_1.1.1' not found
表明指定的 OPENSSL 版本的 .so
库未正确安装。
源码编译安装 OPENSSL 1.1.1,并将其 so 文件放置到可以被系统识别的路径:
## 下在最新版本 1.1.1
wget https://www.openssl.org/source/openssl-1.1.1c.tar.gz
## 上传至 ct-test-ha
scp openssl-1.1.1c.tar.gz ct-test-ha:~/
## 解压并编译安装
tar zxf openssl-1.1.1c.tar.gz
cd openssl-1.1.1c
./config
make test # 执行测试;如果输出 PASS 则继续
make install
## 确保库的引用
ln -s /usr/local/lib64/libssl.so.1.1 /usr/lib64/libssl.so.1.1
ln -s /usr/local/lib64/libcrypto.so.1.1 /usr/lib64/libcrypto.so.1.1
完成后,执行在 EMQX 的 lib 同级目录下执行 ldd lib/crypto-*/priv/lib/crypto.so
,检查是否已能正确识别。如果不在有 not found
的 .so
库,即可正常启动 EMQX。
## 安装包安装
cd emqx
## brew 安装
cd /usr/local/Cellar/emqx/<version>/
查询 crypto
依赖的 .so
动态库列表:
otool -L lib/crypto-*/priv/lib/crypto.so
lib/crypto-4.4.2.1/priv/lib/crypto.so:
/usr/local/opt/openssl@1.1/lib/libcrypto.1.1.dylib (compatibility version 1.1.0, current version 1.1.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.200.5)
检查其显示 OPENSSL 已成功安装至指定的目录:
ls /usr/local/opt/openssl@1.1/lib/libcrypto.1.1.dylib
ls: /usr/local/opt/openssl@1.1/lib/libcrypto.1.1.dylib: No such file or directory
若不存在该文件,则需安装与 otool
打印出来的对应的 OPENSSL 版本,例如此处显示的为 openssl@1.1
:
brew install openssl@1.1
安装完成后,即可正常启动 EMQX。
来源:SSL 连接失败
问题:
客户端无法与 EMQX 建立 SSL 连接。
解决方法:
通常 SSL/TLS 连接握手失败时,EMQX 会在 日志 中输出相应的失败原因,可以借助 EMQX 日志中的关键字来进行简单的问题排查,以下是一些日志中常见的关键字及其对应的含义。EMQX 日志相关内容请参考:日志与追踪。
certificate_expired
:日志中出现 certificate_expired
关键字,说明证书已经过期,请及时续签。no_suitable_cipher
:日志中出现 no_suitable_cipher
关键字,说明握手过程中没有找到合适的密码套件,可能原因有证书类型与密码套件不匹配、没有找到服务端和客户端同时支持的密码套件等等。handshake_failure
:日志中出现 handshake_failure
关键字,原因有很多,可能要结合客户端的报错来分析,例如,可能是客户端发现连接的服务器地址与服务器证书中的域名不匹配。unknown_ca
:日志中出现 unknown_ca
关键字,意味着证书校验失败,常见原因有遗漏了中间 CA 证书、未指定 Root CA 证书或者指定了错误的 Root CA 证书。在双向认证中我们可以根据日志中的其他信息来判断是服务端还是客户端的证书配置出错。如果是服务端证书存在问题,那么报错日志通常为:{ssl_error,{tls_alert,{unknown_ca,"TLS server: In state certify received CLIENT ALERT: Fatal - Unknown CA\n"}}}
看到 CLIENT ALERT
就可以得知,这是来自客户端的警告信息,服务端证书未能通过客户端的检查。
如果是客户端证书存在问题,那么报错日志通常为:
{ssl_error,{tls_alert,{unknown_ca,"TLS server: In state certify at ssl_handshake.erl:1887 generated SERVER ALERT: Fatal - Unknown CA\n"}}}
看到 SERVER ALERT
就能够得知,表示服务端在检查客户端证书时发现该证书无法通过认证,而客户端将收到来自服务端的警告信息。
5. protocol_version
:日志中出现 protocol_version
关键字,说明客户端与服务器支持的 TLS 协议版本不匹配。
当 License 到期时,每次启动节点时都会出现警告以提醒您 License 的到期情况。根据 License 类型,可能会有额外的限制:
如果您是 EMQX 企业版用户,当您的 license 到期时,每次启动节点时都会出现警告以提醒您 license 的到期情况。根据您的 license 类型,可能会有不同的限制:
可以使用以下命令来更新您的 EMQX 企业版 license:
./bin/emqx ctl
license info # 显示 license 信息
license update <License> # 更新 license,<License> 为 license 字符串
还可以通过 Dashboard 来更新 license。有关如何申请 licnese 以及通过 Dashboard 更新许可证,请参见使用 EMQX 企业版 License。
标签: License
申请试用 license 后,License 文件将通过邮件发送,找到附件里的 zip 文件并解压,复制压缩包里的 emqx.lic
文件到 EMQX 的 license 目录.
点击 “Download License” 按钮下载 license, 然后找到您下载的 “license.zip” 文件并解压.
从压缩文件中提取许可证文件(emqx.lic)到 EMQX 用户可读的目录中。
在提取完成后,需要从命令行重新加载许可证以完成更新。
拷贝完成后需要通过命令行重新加载 license 以完成更新:
基础命令:
emqx_ctl license reload [license 文件所在路径]
不同安装方式更新命令如下:
## 适用于 zip 包
./bin/emqx_ctl license reload path/to/emqx.lic
## DEB/RPM 包安装
emqx_ctl license reload path/to/emqx.lic
## Docker 镜像安装
docker exec -it emqx-ee emqx_ctl license reload path/to/emqx.lic
提示
在一个多节点集群中,emqx_ctl license reload
命令只需要在其中一个节点上执行,因为许可证将被复制并应用到所有成员。 每一个节点都会在配置好的 EMQX 的数据目录下包含一份新的许可证,以及一份旧的许可证的备份(如果有的话)。
注意:对于e4.3.10之前的EMQX版本,此命令只在执行命令的本地节点上生效,所以对于那些旧版本,此命令需要在集群的每个节点上执行。
来源:EMQX 支持私有协议进行扩展吗?如支持应该如何实现?
对于新开发的私有协议,EMQX 提供一套 TCP 协议接入规范,私有协议可以按照该规范进行开发接入。如果所使用的协议已经定型或协议底层非 TCP,可以通过网关进行转换处理,之后通过 MQTT 协议接入 EMQX,或直接联系 EMQ 官方支持私有协议适配。
EMQX 企业版可以通过以下的三种方式捕获设备的上下线的事件:
$SYS
主题:
$SYS/brokers/{node}/clients/${clientid}/connected
$SYS/brokers/{node}/clients/${clientid}/disconnected
来源:我想限定某些主题只为特定的客户端所使用,EMQX 该如何进行配置?
EMQX 支持限定客户端可以使用的主题,从而实现设备权限的管理。如果要做这样的限定,需要在 EMQX 启用 ACL(Access Control List),并禁用匿名访问和关闭无 ACL 命中的访问许可(为了测试调试方便,在默认配置中,后两项是开启的,请注意关闭)。
## etc/emqx.conf
## ACL nomatch
mqtt.acl_nomatch = allow
ACL 可以配置在文件 etc/acl.conf
中,或者配置在后台数据库中。下面例子是 ACL 控制文件的一个配置行,含义是用户 “dashboard” 可以订阅 “$SYS/#” 主题。ACL 在后台数据库中的配置思想与此类似,详细配置方法请参阅 EMQX 文档的 ACL 访问控制 章节。
{allow, {user, "dashboard"}, subscribe, ["$SYS/#"]}.
支持。EMQX 中的授权管理机制可以实现对客户端权限的精细管理。
EMQX 默认从 acl.conf
文件中查询 ACL 规则,用户也可以配置数据库作为 ACL 规则的数据源,例如 EMQX 的内置数据库、MySQL、Redis 等。
通常我们推荐在 acl.conf
中添加针对多个客户端生效的规则,例如仅允许同一网段内的客户端订阅系统主题;在数据库中添加针对单个客户端生效的规则,例如允许客户端 client1 订阅主题 example。
授权的完整介绍可参考 此处。
能。目前 EMQX 支持连接速率和消息率控制,请参考 速率限制。
支持。EMQX 支持连接速率和消息流入速率控制,从入口处避免系统过载,完整介绍可参考 此处。
EMQX 企业版支持消息持久化,可以将消息保存到数据库,请参考数据集成。
可以的。EMQX 提供的 HTTP API 中包含断开 MQTT 连接,该操作在 EMQX 2.x 和 3.0+ 的实现方式有所不同:
调用的 API 如下所示:
HTTP 方法:DELETE
URL:api/[v2|v3]/clients/{clientid}
<!--请注意区分 URL 中第二部分的版本号,请根据使用的版本号来决定 -->
返回内容:
{
"code": 0,
"result": []
}
HTTP API 使用方式参考 管理监控API (HTTP API)。
能。目前 EMQX 企业版提供了内置的 Kafka 桥接方式,支持把消息桥接至 Kafka 进行流式处理。EMQX 使用 Kafka 参照 Sink - Apache Kafka。
来源:EMQX 企业版中桥接 Kafka,一条 MQTT 消息到达 EMQX 集群之后就回 MQTT Ack 报文还是写入 Kafka 之后才回 MQTT Ack 报文?
标签: Kafka 配置
取决于 Kafka 桥接的配置,配置文件位于/etc/emqx/plugins/emqx_bridge_kafka.conf
## Pick a partition producer and sync/async.
bridge.kafka.produce = sync
如果运行期间,后端的 Kafka 服务不可用,则消息会被累积在 EMQX 服务器中,
因此建议做好 Kafka 服务的监控,在发现 Kafka 服务有异常情况的时候尽快恢复 Kafka 服务。
EMQX 支持集群自动发现。集群可以通过手动配置或自动配置的方式实现,参考 创建集群。
来源:我可以把 MQTT 消息从 EMQX 转发其他消息中间件吗?例如 RabbitMQ?
EMQX 支持转发消息到其他消息中间件,通过 EMQX 提供的桥接方式就可以做基于主题级别的配置,从而实现主题级别的消息转发。请参考数据集成。
来源:我可以把消息从 EMQX 转到公有云 MQTT 服务上吗?比如 AWS 或者 Azure 的 IoT Hub?
EMQX 可以转发消息到标准 MQTT Broker,包括其他 MQTT 实现、公有云的 IoT Hub,通过 EMQX 提供的MQTT桥接就可以实现。
来源:MQTT Broker(比如 Mosquitto)可以转发消息到 EMQX 吗?
Mosquitto 可以配置转发消息到 EMQX,请参考Sink - MQTT。
EMQX 支持追踪来自某个客户端的报文或者发布到某个主题的报文。追踪消息的发布和订阅需要使用命令行工具(emqx ctl)的 trace 命令,下面给出一个追踪‘topic’主题的消息并保存在 trace_topic.log
中的例子。更详细的说明请参阅 日志追踪。
来源:为什么我做压力测试的时候,连接数目和吞吐量老是上不去,有系统调优指南吗?
在做压力测试的时候,除了要选用有足够计算能力的硬件,也需要对软件运行环境做一定的调优。比如修改操作系统的全局最大文件句柄数,允许用户打开的文件句柄数,TCP 的 backlog 和 buffer,Erlang 虚拟机的进程数限制等等。甚至包括需要在客户端上做一定的调优以保证客户端可以有足够的连接资源。
系统的调优在不同的需求下有不同的方式,在 EMQX 的 系统调优 中对用于普通场景的调优有较详细的说明。
标签: 性能测试
在EMQ 2.0版本发布的时候,由第三方软件测试工具服务提供商 XMeter 执行了一次百万级别连接的性能测试。测试基于开源社区中最流行的性能测试工具 Apache JMeter,以及开源性能测试插件。该性能测试场景为测试客户端到服务器端的MQTT协议连接,该测试场景下除了发送MQTT协议的控制包和PING包(每5分钟发送一次)外,不发送用户数据,每秒新增连接数为1000,共计运行30分钟。
在该测试中,还执行了一些别的性能测试,主要为在为10万MQTT背景连接的情况下,执行了不同条件下的消息发送和接收的场景。具体请参见性能测试报告。
EMQX 支持加密连接。在生产环境部署时,推荐的方案是使用负载均衡终结 TLS。通过该方式,设备端和服务器端(负载均衡)的采用加密的连接,而负载均衡和后端的 EMQX 节点采用一般的 TCP 连接。
推荐以集群方式部署 EMQX,并在集群前端部署负载均衡(Nginx、HAProxy 等)使连接均衡地落到集群的每个节点上。
对于通信安全有较高要求的用户,我们建议为客户端启用 TLS 连接,并在 LB 侧终结 TLS 连接,即客户端与 LB 之间采用 TLS 加密通信,LB 与 EMQX 节点之间则仍然采用 TCP 通信。
由于 EMQX 节点并不对公网暴露端口,因此并不会降低整体的安全性,但通过 TLS 卸载,可以有效节省 EMQX 的资源消耗。
执行 $ emqx console
,查看输出内容
logger
命令缺失$ emqx console
Exec: /usr/lib/emqx/erts-10.3.5.1/bin/erlexec -boot /usr/lib/emqx/releases/v3.2.1/emqx -mode embedded -boot_var ERTS_LIB_DIR /usr/lib/emqx/erts-10.3.5.1/../lib -mnesia dir "/var/lib/emqx/mnesia/emqx@127.0.0.1" -config /var/lib/emqx/configs/app.2019.07.23.03.07.32.config -args_file /var/lib/emqx/configs/vm.2019.07.23.03.07.32.args -vm_args /var/lib/emqx/configs/vm.2019.07.23.03.07.32.args -- console
Root: /usr/lib/emqx
/usr/lib/emqx
/usr/bin/emqx: line 510: logger: command not found
解决办法:
Centos/Redhat
$ yum install rsyslog
Ubuntu/Debian
$ apt-get install bsdutils
openssl
缺失 $ emqx console
Exec: /emqx/erts-10.3/bin/erlexec -boot /emqx/releases/v3.2.1/emqx -mode embedded -boot_var ERTS_LIB_DIR /emqx/erts-10.3/../lib -mnesia dir "/emqx/data/mnesia/emqx@127.0.0.1" -config /emqx/data/configs/app.2019.07.23.03.34.43.config -args_file /emqx/data/configs/vm.2019.07.23.03.34.43.args -vm_args /emqx/data/configs/vm.2019.07.23.03.34.43.args -- console
Root: /emqx
/emqx
Erlang/OTP 21 [erts-10.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:32] [hipe]
{"Kernel pid terminated",application_controller,"{application_start_failure,kernel,{{shutdown,{failed_to_start_child,kernel_safe_sup,{on_load_function_failed,crypto}}},{kernel,start,[normal,[]]}}}"}
Kernel pid terminated (application_controller) ({application_start_failure,kernel,{{shutdown,{failed_to_start_child,kernel_safe_sup,{on_load_function_failed,crypto}}},{kernel,start,[normal,[]]}}})
Crash dump is being written to: log/crash.dump...done
解决办法: 安装 1.1.1 以上版本的 openssl
。
来源:EMQX 中 ssl resumption session 的使用
修改 emqx.conf 配置中的 reuse_sessions = on 并生效后。如果客户端与服务端通过 SSL 已经连接成功,当第二次遇到客户端连接时,会跳过 SSL 握手阶段,直接建立连接,节省连接时间,增加客户端连接速度。
执行 emqx ctl listeners
,查看对应端口下的 shutdown_count
统计。
客户端断开链接错误码列表:
keepalive_timeout
:MQTT keepalive 超时。closed
:TCP 客户端断开连接(客户端发来的 FIN,但没收到 MQTT DISCONNECT)。normal
:MQTT 客户端正常断开。einval
:EMQX 想向客户端发送一条消息,但是 Socket 已经断开。function_clause
:MQTT 报文格式错误。etimedout
:TCP 发送超时(没有收到 TCP ACK 回应)。proto_unexpected_c
:在已经有一条 MQTT 连接的情况下重复收到了 MQTT 连接请求。idle_timeout
: TCP 连接建立 15s 之后,还没收到 connect 报文。可以在 Shell 中执行 emqx ctl listeners
,该命令会返回每个监听器上连接断开的统计信息,包含断开原因与断开次数。例如:
$ ./bin/emqx ctl listeners
tcp:default
listen_on : 0.0.0.0:1883
acceptors : 16
proxy_protocol : false
running : true
current_conn : 9
max_conns : infinity
shutdown_count : [{keepalive_timeout,1},{idle_timeout,2}]
以下是一些常见的断开原因:
keepalive_timeout
:客户端心跳超时而被 EMQX 关闭连接。tcp_closed
:客户端在未发送 DISCONNECT 报文的情况下直接关闭了 TCP 连接。frame_too_large
:客户端发送的报文大小超过了最大报文限制而被 EMQX 关闭连接。protocol_error
:客户端的行为不符合协议规范而被 EMQX 关闭连接,例如客户端在同一个连接内发送了多个 CONNECT 报文。idle_timeout
:TCP 连接建立的 15 秒内,EMQX 未收到客户端发送的 CONNECT 报文,因此关闭了连接。也可以使用 日志追踪 功能追踪所有与你指定的 Client ID、IP、Topic 相关的日志,然后你可以基于这些日志来分析客户端连接断开的原因。
来源:常见日志原因
关键字:“reason: {shutdown, takeovered}”
在 EMQX 中,每个 MQTT 客户端连接都由独立的一个进程来维护。每当有 MQTT 客户端连接,EMQX 就会创建一个新的进程。如果客户端希望从已存在的会话状态中恢复通信,那么 EMQX 就需要将旧进程中的会话状态数据迁移至新进程。这个过程,我们称之为 Take over,而旧进程将在接管工作完成后关闭,关闭的原因为 {shutdown, takeovered}
。如果原先的客户端仍处于连接状态,那么 EMQX 还将发送一个 Reason Code 为 0x8E (Session taken over) 的 DISCONNECT 报文,然后关闭旧连接。
关键字:“reason: {shutdown, discarded}”
与会话接管相反,如果客户端在连接时表示希望开始一个全新的会话,那么 EMQX 就需要丢弃旧进程中的会话状态数据,EMQX 将以 {shutdown, discarded}
原因关闭旧进程。如果原先的客户端仍处于连接状态,那么 EMQX 同样将发送一个 Reason Code 为 0x8E (Session taken over) 的 DISCONNECT 报文,然后关闭旧连接。
关键字:“reason: {shutdown, kicked}”
表示客户端连接被手动踢除(在 Dashboard 上点击 Kick Out 按钮或者调用 DELETE clients/{clientid} API)
。如果原先的客户端仍处于连接状态,那么 EMQX 同样将发送一个 Reason Code 为 0x98 (Administrative action) 的 DISCONNECT 报文,然后关闭旧连接。
关键字:“reason: {shutdown, tcp_closed}”
这一日志表示客户端在没有发送 DISCONNECT 报文的情况下直接关闭了网络连接,与之对应的是 reason: {shutdown, normal}
,这表示客户端先发送了一个 Reason Code 为 0 的 DISCONNECT 报文,然后再关闭了网络连接。
所以,当发现 EMQX 日志中出现 tcp_closed
,并且这一断开连接的行为并不是你期望的时,建议排查客户端的实现是否存在问题。
关键字:“maximum heap size reached”
出现这一日志说明当前客户端进程占用的堆栈已经超过了预设的最大值,客户端进程将被强制杀死以保证 EMQX 的可用性,避免客户端进程的内存占用无限制增长最终导致 EMQX OOM。
通常消息堆积是导致客户端进程堆栈占用上升的主要原因,而消息堆积通常是因为客户端消费能力与对端消息的生产能力不匹配,推荐的解决方案是优化客户端处理代码或者使用共享订阅分散负载。
与此相关的配置项是 force_shutdown_policy
,它的配置格式为 <Maximum Message Queue Length>|<Maximum Heap Size>
,例如 10000|64MB
。其中 <Maximum Heap Size>
就是限制每个客户端进程能够占用的最大堆栈内存。
关键字:“Parse failed for function_clause”
这一日志表示报文解析失败。可能因为这不是一个 MQTT 报文,我们遇到过很多向 MQTT 端口发送 HTTP 请求的情况,也可能因为报文中包含了非 UTF-8 字符等等。我们可以在这条 “Parse failed…” 日志中检索 Frame data
关键字以查看完整的报文,帮助我们分析解析失败的可能原因。
关键字:“Dropped msg due to mqueue is full”
EMQX 可以同时发送多个未确认的 QoS 1 和 QoS 2 消息,但受限于客户端的性能,通常我们会限制允许同时发送的消息的最大数量。在达到这一限制后,后续到达的消息都会被 EMQX 缓存在每个客户端进程的消息队列中。为了防止消息缓存过多,EMQX 同样为消息队列设置了最大长度限制。如果消息到达时消息队列已满,那么这个最新的消息仍然会入队,但队列中最老的消息将会出队并被丢弃,同时产生 “Dropped msg due to mqueue is full” 这一日志。
如果此日志只是在流量高峰期出现,那么可以修改配置项 max_mqueue_len
仅增加消息队列的最大长度。但如果持续出现,那么说明当前客户端消费能力较差,尽量选择优化客户端代码或者使用共享订阅分散负载。
EMQX 对资源的使用主要有以下的影响因素,每个因素都会对计算和存储资源的使用产生影响:
另外,如果设备通过 TLS(加密的连接)连接 EMQX,EMQX 会需要额外的资源(主要是 CPU 资源)。推荐方案是在 EMQX 前面部署负载均衡,由负载均衡节点卸载 TLS,实现职责分离。
来源:我的连接数目并不大,EMQX 生产环境部署需要多节点吗?
即使在连接数量,消息率不高的情况下(服务器低负载),在生产环境下部署多节点的集群依然是很有意义的。集群能提高系统的可用性,降低单点故障的可能性。当一个节点宕机时,其他在线节点可以保证整个系统的服务不中断。
来源:当我遇到客户端连接、发布、订阅相关的问题时应该怎么办?
EMQX 的 Debug 日志基本记录了所有的行为和现象,通过阅读 Debug 日志我们能够知道客户端何时发起了连接,连接时指定了哪些参数,连接是否通过,被拒绝连接的原因是什么等等。但是由于 Debug 日志记录的信息过多,会带来额外的资源消耗,并且不利于我们针对单个客户端或主题进行分析。
所以 EMQX 提供了日志追踪功能,我们可以指定想要追踪的客户端或主题,EMQX 会将所有与该客户端或主题相关的 Debug 日志都输出到指定日志文件中。这样不管是自己分析调试,还是寻求社区帮助,都会方便许多。
需要注意的是,如果客户端是因为网络原因而无法连接到 EMQX 的话,日志追踪功能也是无法提供帮助的,因为此时 EMQX 尚未收到任何报文。这种情况很多时候是因为防火墙、安全组等网络配置原因导致服务器端口没有开放,这在使用云主机部署 EMQX 时尤为常见。所以除了日志追踪,我们可以通过检查端口占用、监听情况,检查网络配置等手段来排除网络方面的原因。
来源:为什么会出现 Client ID 为 CENSYS 的或者是其他我不认识的客户端?
CENSYS 是一款互联网探测扫描工具,它会周期性扫描 IPv4 地址空间,探测 HTTP、SSH、MQTT 等协议的默认端口。所以如果你发现有 Client ID 为 CENSYS 的或者其他未知的客户端接入了你的 MQTT Broker,这意味你目前处于相对较低的安全性保障下。以下措施可以有效帮助你避免这个问题:
可以,某些系统消息的发布频率可能较高,例如客户端上下线事件,所以采用共享订阅对于客户端来说是非常必要的。订阅示例: $share/group1/$SYS/brokers/+/clients/+/connected
。
MQTT 协议规定了服务端不能发送保留消息给共享订阅。
在共享订阅者的连接断开,但会话仍然存在的情况下,服务端会仍然向该共享订阅者投递消息,只是消息将被暂存至对应的会话中,这会导致其余在线的共享订阅者看起来没能完整地消费到所有消息。另外,如果该共享订阅者在重连时选择创建全新会话,那么缓存在旧会话中的消息就会永久丢失。
如果我们确认了不存在以上情况,而消息丢失的问题仍然存在,那么可以借助 EMQX 的客户端追踪功能来进行进一步的排查。
来源:EMQX 启动时提示端口被占用(eaddrinuse)应该怎么办?
默认情况下,EMQX 启动时会占用 7 个端口,它们分别是:
BasePort (4370) + Offset
,4370 固定无法修改,Offset 则由节点名称(Name@Host
)中 Name 部分的数字后缀决定,没有数字后缀则默认为 0。例如 emqx@127.0.0.1
的 Offset 为 0,emqx1@127.0.0.1
的 Offset 为 1。BasePort (5370) + Offset
,5370 固定无法修改,Offset 则由节点名称(Name@Host
)中 Name 部分的数字后缀决定,没有数字后缀则默认为 0。来源:为什么 EMQX 启动时会输出日志 “WARNING: Default (insecure) Erlang cookie is in use.” 应该怎么办?
完整的 WARNING 日志如下:
WARNING: Default (insecure) Erlang cookie is in use.
WARNING: Configure node.cookie in /usr/lib/emqx/etc/emqx.conf or override from environment variable EMQX_NODE__COOKIE
WARNING: NOTE: Use the same cookie for all nodes in the cluster.
只有使用相同 Cookie 的 EMQX 节点才能组成一个集群。虽然 Cookie 并不能保证集群的通信安全,但它可以避免节点连接到它不打算与之通信的集群。EMQX 节点默认统一将 emqxsecretcookie
作为 Cookie,所以我们会推荐用户在搭建集群时更改 Cookie 的值。
第二条 WARNING 日志则提示了修改 Cookie 的两种方式,分别为 emqx.conf
配置文件中的 node.cookie
,和环境变量 EMQX_NODE__COOKIE
。
来源:使用 Docker 部署 EMQX 时,为什么容器重启会导致配置的规则、资源等数据丢失?
EMQX 的运行时数据,譬如规则和资源的配置、保留消息等等,它们都存储在 /opt/emqx/data
目录下,所以为了保证这部分数据不会因容器重启而丢失,我们需要将 /opt/emqx/data
目录挂载到本地主机目录或者数据卷中去。
但我们可能会发现,即便已经挂载了 /opt/emqx/data
目录,数据仍然可能会在容器重启后丢失。这是因为 EMQX 的运行时数据实际上存储在 /opt/emqx/data/mnesia/${Node Name}
目录。所以数据丢失,实际上是容器重启后 EMQX 的节点名发生了变化,进而导致 EMQX 创建了一个全新的存储目录。
EMQX 节点名由 Name 和 Host 两部分组成,其中 Host 默认来自容器的 IP 地址。在默认的网络配置下容器一旦重启它的 IP 就可能发生变化,所以我们要做的就是让容器的 IP 保持固定。
EMQX 提供了一个环境变量 EMQX_HOST
,允许我们自行设置节点名的 Host 部分。当然前提是这个 Host 必须是其他节点可以连接的,所以我们还需要配合网络别名使用:
docker run -d --name emqx -p 18083:18083 -p 1883:1883 -e EMQX_HOST=alias-for-emqx --network example --network-alias alias-for-emqx --mount type=bind,source=/tmp/emqx,target=/opt/emqx/data emqx:5.0.24
来源:EMQX 启动失败,日志提示 “libatomic.so.1: cannot open shared object file: No such file or directory”
这个错误的原因是当前系统缺少了 libatomic 这个依赖项,解决办法是安装这个依赖项:
# Rocky Linux, CentOS, ...
yum install -y libatomic
# Debian, Ubuntu, ...
apt install -y libatomic
如果你手动安装 RPM 或 DEB 包,可能会在安装时就遇到以下提示:
$ rpm -ivh emqx-5.7.0-el8-amd64.rpm
error: Failed dependencies:
libatomic is needed by emqx-5.7.0-el8-amd64.rpm
解决办法仍然是先手动安装 libatomic 这个依赖项。
当然,我们最推荐的安装方式是使用包管理器(yum、apt 等)安装,它们会自动安装所需依赖,不用我们额外操心。
来源:使用 Docker 启动 EMQX 失败,日志提示 “Permission denied”
当你打算以目录挂载的方式持久化 EMQX 数据时:
sudo docker run -d --name emqx -p 18083:18083 -p 1883:1883 -v /emqx/data:/opt/emqx/data -v /emqx/log:/opt/emqx/log emqx:latest
你可能会遇到容器启动失败,并提示以下错误:
mkdir: cannot create directory '/opt/emqx/data/configs': Permission denied
这是因为 EMQX 在容器中以 Linux 用户 emqx
运行,而你宿主机中的目录却可能是在 root
用户下创建的,因此 EMQX 自然无法在这些目录下创建目录或文件。
想要解决这个问题,你可以在宿主机中创建一个 emqx
用户,然后由该用户来创建需要挂载的目录,或者直接将已经创建好的 data、log 目录的权限修改为 777。
当然,最推荐的方式还是以命名数据卷的方式实现 EMQX 数据持久化,这样就不必再关心权限问题:
sudo docker volume create --name emqx-data
sudo docker volume create --name emqx-log
sudo docker run -d --name emqx -p 18083:18083 -p 1883:1883 -v emqx-data:/opt/emqx/data -v emqx-log:/opt/emqx/log emqx:latest
理论上没有限制,但是为了提高消息订阅和发布的性能,需要避免过多的 ACL 规则,建议单个客户端 ACL 不要超过 10 条,可以使用通配符规则减少 ACL 条数。
来源:什么是系统主题?
EMQX 会周期性地发布自身运行状态、MQTT 报文收发计数和客户端上下线事件等消息到 $SYS/
开头的系统主题,客户端可以订阅系统主题来获取相关的信息。
系统主题的完整介绍可参考 此处。
EMQX 提供了一个 ExProto 网关,支持用户使用其熟悉的编程语言(如 Java、Python、Go 等)开发 gRPC 服务,用于解析设备使用的自定义协议,并完成设备连接、身份验证和消息传输等功能。
详情可参考 ExProto 协议网关。
EMQX 或 MQTT 协议并没有直接对每个客户端的消息接收速率进行严格限制。但当接收的消息过多,客户端无法及时处理时,可能会导致消息堆积过多并最终丢失。为了确保系统稳定性和消息传输的可靠性,建议每个订阅客户端的消息接收速率不超过 1500 消息/秒(按每条消息 1KB 计算)。
如果消息接收速率超过此建议,可以使用共享订阅来添加多个订阅客户端,从而分散负载,降低单个订阅客户端的消息接收速率。
来源:EMQX 支持用户在服务端侧主动断开 MQTT 连接吗?
支持。EMQX 提供了 命令行接口 emqx ctl clients kick <Client ID>
与 REST API DELETE /clients/{clientid}
,允许用户手动踢除 MQTT 连接,用户也可以在 Dashboard 的客户端列表页完成此操作。
详细的 REST API 使用,请参考开源版 API 文档和企业版 API 文档。
EMQX 提供了三种监听设备上下线事件的方式:
client.connected
和 client.disconnected
事件,配合 数据集成 功能将事件消息写入到指定数据库。(仅限企业版)来源:服务端使用 MQTT 与 EMQX 集成时,如何提高数据吞吐性能和可靠性?
如果应用服务使用 MQTT 协议与 EMQX 进行数据集成,在这种场景下,每个客户端通常需要承担较高的负载。为了充分发挥客户端的性能并确保系统的可用性,以下是一些最佳实践建议:
核心原则是降低单个客户端处理的消息量。通过使用 MQTT 多通道交互,不仅可以提升整体消息吞吐性能,还能增强系统的高可用性。
Dashboard 的默认用户名和密码分别是 admin 和 public,出于安全考虑,在您首次登录时,Dashboard 会强制要求你修改密码。如果你忘记了之前设置的密码,你可以使用以下命令来设置新密码,并无需提供就密码:
emqx ctl admin <Username> <New Password>
现象
Windows 执行 ./bin/emqx console
弹出错误窗口:
无法启动次程序,因为计算机中丢失 MSVCR120.dll。请尝试重新安装该程序以解决此问题。
解决方法
安装 Microsoft Visual C++ RedistributablePackage
典型的物联网平台包括设备硬件、数据采集、数据存储、分析、Web / 移动应用等。EMQX 位于数据采集这一层,分别与硬件和数据存储、分析进行交互,是物联网平台的核心:前端的硬件通过 MQTT 协议与位于数据采集层的 EMQX 交互,通过 EMQX 将数据采集后,通过 EMQX 提供的数据接口,将数据保存到后台的持久化平台中(各种关系型数据库和 NOSQL 数据库),或者流式数据处理框架等,上层应用通过这些数据分析后得到的结果呈现给最终用户。
标签: 认证鉴权
认证鉴权指的是当一个客户端连接到 MQTT 服务器的时候,通过服务器端的配置来控制客户端连接服务器的权限。EMQ 的认证机制包含了有三种:
通过用户名密码、ClientID 认证的方式除了通过配置文件之外,还可以通过各类数据库和外部应用来配置,比如 MySQL、PostgreSQL、Redis、MongoDB、HTTP 和 LDAP 等。
来源:为什么开启认证后,客户端还是可以不需要用户名密码就能连接?
标签: 认证鉴权
EMQX 支持匿名认证,并默认启用。开启认证后,未指定用户名密码的客户端将以匿名认证的方式成功登录。想要更改这一行为,需要修改 emqx.conf 文件中的 allow_anonymous
配置项,将其设为 false,然后重启 EMQX 即可。
注:如果您的客户端连接的是 11883 端口,则需要修改 zone.internal.allow_anonymous
,Zone 与 Listener 的相关知识可参阅 配置说明。
标签: WebHook
钩子(hook)指的是由 EMQX 在连接、对话和消息触发某些事件的时候提供给对外部的接口,主要提供了如下的钩子,EMQX 提供了将这些 hook 产生的事件持久化至数据库的功能,从而很方便地查询得知客户端的连接、断开等各种信息。
client.connected
:客户端上线。client.disconnected
:客户端连接断开。client.subscribe
:客户端订阅主题。client.unsubscribe
:客户端取消订阅主题。session.created
:会话创建。session.resumed
:会话恢复。session.subscribed
:会话订阅主题后。session.unsubscribed
:会话取消订阅主题后。session.terminated
:会话终止。message.publish
:MQTT 消息发布。message.delivered
:MQTT 消息送达。message.acked
:MQTT 消息回执。message.dropped
:MQTT 消息丢弃。标签: MySQL 认证
提示:
4.3 已兼容 caching_sha2_password,该问题仅在 4.3 以下的版本中出现。
不同于以往版本,MySQL 8.0 对账号密码配置默认使用caching_sha2_password
插件,需要将密码插件改成mysql_native_password
。
## 切换到 mysql 数据库
mysql> use mysql;
## 查看 user 表
mysql> select user, host, plugin from user;
+------------------+-----------+-----------------------+
| user | host | plugin |
+------------------+-----------+-----------------------+
| root | % | caching_sha2_password |
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session | localhost | caching_sha2_password |
| mysql.sys | localhost | caching_sha2_password |
| root | localhost | caching_sha2_password |
+------------------+-----------+-----------------------+
## 修改密码插件
mysql> ALTER USER 'your_username'@'your_host' IDENTIFIED WITH mysql_native_password BY 'your_password';
Query OK, 0 rows affected (0.01 sec)
## 刷新
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)
my.conf
。default_authentication_plugin=mysql_native_password
来源:EMQX Enterprise 和 EMQX 的主要区别是什么?
标签: 企业版
EMQX Enterprise 基于 EMQX,包含了 EMQX 的所有功能。与 EMQX 相比,主要有以下方面的区别:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。