在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文?
- 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。
- 零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。
- 灰度发布:根据 IP/地理位写入不同后缀,让同一客户端同时连到测试与生产集群。
- 兼容旧设备:早期固件不支持密码,网关可注入随机密码并回传至 IAM。
2、指令速查
指令 | 作用 | 典型配置 |
---|---|---|
mqtt on; | 在虚拟主机启用 MQTT 过滤 | server { mqtt on; … } |
mqtt_buffers N S; (1.25.1) | 单连接缓存 N × S 字节处理 PUBLISH/ACK | mqtt_buffers 256 2k; |
mqtt_set_connect field value; | 重写 CONNECT 字段(clientid / username / password) | mqtt_set_connect clientid "$geoip_city_$remote_addr"; |
弃用:
mqtt_rewrite_buffer_size
≤ 1.25.0;新版本统一用 mqtt_buffers。
3、最小示例:按照 IP 段自动打上租户前缀
# 自动映射租户
geo $remote_addr $tenant {default default_;10.0.0.0/16 corpA_;172.16.0.0/12 corpB_;
}stream {server {listen 18883;proxy_pass 10.0.0.20:1883; # 后端 EMQXmqtt on;# ① 每连接缓存 256×2 KB,适配高并发发布mqtt_buffers 256 2k;# ② 重写 ClientID / Usernamemqtt_set_connect clientid "$tenant${mqtt_preread_clientid}";mqtt_set_connect username "$tenant${mqtt_preread_username}";}
}
效果
接入设备 sensor01
若来自 10.1.2.3
,Gateway 转发的实际 ClientID 将变为 corpA_sensor01
。后端可据此分配独立 ACL 与 Topic。
4、高级玩法
需求 | 配置要点 |
---|---|
灰度:5 % 设备流向新集群 | map $mqtt_preread_clientid $bucket { … } + 两条 proxy_pass |
密码托管:IoT Hub 统一颁发 | mqtt_set_connect password "$secure_token"; |
QoS 带宽保护 | 配合 limit_conn_zone / limit_rate 控制单租户并发与速率 |
TLS 终端 | ssl_preread on; → Nginx 解 SNI,再走 MQTT 过滤 |
5、性能调优
场景 | 建议 |
---|---|
大量小消息(<= 256 B) | mqtt_buffers 512 512; 减少 re-alloc |
高并发长连接(≥ 50 K) | 缩小 buffer,配合系统 net.core.somaxconn |
频繁改写大字段 | 保持 mqtt_buffers N S ≥ 字段最大长度,防止 rewrite too long 错误 |
观测重写结果 | access_log 增加 $mqtt_preread_clientid $mqtt_preread_username |
6、安全注意事项
- 字段长度:MQTT 规范 ClientID ≤ 23 字节(3.1.1),> 23 会被后端拒绝;重写前需
if ($clientid ~ "^.{1,23}$") { … }
。 - 密码明文:模块不自动加密;建议启用 TLS + Password Hash。
- 回环风险:若网关与 Broker 在同一台 Nginx,需要拆分端口或使用
proxy_bind
。
7、结语
ngx_stream_mqtt_filter_module
把「MQTT 连接级改写」下沉到 L4,配合 Stream 生态(keyval
/ limit_conn
/ js
等)即可实现动态租户隔离、无感鉴权与流量灰度。对于千万级设备同时在线、高频推送的 IoT 平台,它既保证极低延迟,又提供丰富的运营 & 安全钩子 —— 只需几行配置,你的 Nginx 就能变身“智能 MQTT 接入网关”。