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/ACKmqtt_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;   # 后端 EMQX

        mqtt on;

        # ① 每连接缓存 256×2 KB,适配高并发发布
        mqtt_buffers 256 2k;

        # ② 重写 ClientID / Username
        mqtt_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、安全注意事项

  1. 字段长度:MQTT 规范 ClientID ≤ 23 字节(3.1.1),> 23 会被后端拒绝;重写前需 if ($clientid ~ "^.{1,23}$") { … }
  2. 密码明文:模块不自动加密;建议启用 TLS + Password Hash。
  3. 回环风险:若网关与 Broker 在同一台 Nginx,需要拆分端口或使用 proxy_bind

7、结语

ngx_stream_mqtt_filter_module 把「MQTT 连接级改写」下沉到 L4,配合 Stream 生态(keyval / limit_conn / js 等)即可实现动态租户隔离、无感鉴权与流量灰度。对于千万级设备同时在线、高频推送的 IoT 平台,它既保证极低延迟,又提供丰富的运营 & 安全钩子 —— 只需几行配置,你的 Nginx 就能变身“智能 MQTT 接入网关”。

更多推荐