博客 | NGINX

使用 NGINX Plus 优化企业环境中的 MQTT 部署

NGINX-F5-horiz-black-type-RGB 的一部分
Michael Vernik 缩略图
迈克尔·韦尔尼克
2023 年 6 月 5 日发布
Prabhat Dixit 缩略图
帕拉巴特·迪克西特
2023 年 6 月 5 日发布

在宣布NGINX Plus R29 版本时,我们简要介绍了其对解析MQTT消息的全新原生支持。 在这篇文章中,我们将在此基础上讨论如何配置 NGINX Plus 以优化企业环境中的 MQTT 部署。

什么是 MQTT?

MQTT 代表消息队列遥测传输。 它是一种非常流行的轻量级发布-订阅消息协议,非常适合通过互联网连接物联网 (IoT) 或机器对机器 (M2M) 设备和应用。 MQTT 旨在在低带宽或低功耗环境中高效运行,使其成为具有大量远程客户端的应用的理想选择。 它应用于各种行业,包括消费电子、汽车、运输、制造和医疗保健。

NGINX Plus MQTT 消息处理

NGINX Plus R29 支持MQTT 3.1.1MQTT 5.0 。 它充当客户端和代理之间的代理,卸载核心系统的任务,简化可扩展性,并降低计算成本。 具体来说,NGINX Plus 解析并重写 MQTT CONNECT消息的部分内容,从而实现以下功能:

  • MQTT 代理负载均衡 
  • 会话持久性(将客户端重新连接到同一个代理) 
  • SSL/TLS 终止 
  • 客户端证书身份验证 

MQTT 消息处理指令必须在 NGINX 配置文件的上下文中定义,并由ngx_stream_mqtt_preread_module提供
ngx_stream_mqtt_filter_module

预读模块在 NGINX 内部代理之前处理 MQTT 数据,允许根据解析的消息数据做出负载均衡和上游路由决策。

过滤器模块可以重写接收到的 CONNECT 消息中的clientidusernamepassword字段。 将这些字段设置为变量和复杂值的能力大大扩展了配置选项,使 NGINX Plus 能够屏蔽敏感的设备信息或插入 TLS 证书可分辨名称等数据。

MQTT 指令和变量

现在有几种新的指令和嵌入式变量可用于调整您的 NGINX 配置,以优化 MQTT 部署并满足您的特定需求。

预读模块指令和嵌入变量

  • mqtt_preread – 启用 MQTT 解析,从客户端设备发送的 CONNECT 消息中提取clientidusername字段。 这些值通过嵌入变量提供,并帮助哈希会话对上游服务器进行负载平衡(见下面的示例)。
  • $mqtt_preread_clientid – 表示设备发送的 MQTT 客户端标识符。
  • $mqtt_preread_username — 代表客户端为身份验证目的发送的用户名。

过滤模块指令

  • mqtt – 定义是否启用 MQTT 重写。
  • mqtt_buffers – 覆盖每个连接可分配的 MQTT 处理缓冲区的最大数量以及每个缓冲区的大小。 默认情况下,NGINX 将对每个连接施加 100 个缓冲区的限制,每个缓冲区的长度为 1k。 通常,这对于性能来说是最佳的,但在特殊情况下可能需要进行调整。 例如,较长的 MQTT 消息需要更大的缓冲区大小。 在短时间内处理给定连接的大量 MQTT 消息的系统可能会受益于增加的缓冲区数量。 在大多数情况下,调整缓冲区参数对底层系统性能影响不大,因为 NGINX 从内部内存池构建缓冲区。
  • mqtt_rewrite_buffer_size – 指定用于构建 MQTT 消息的缓冲区的大小。 此指令已被弃用并且自 NGINX Plus R30 起已过时。
  • mqtt_set_connect – 重写从客户端发送的 CONNECT 消息的参数。 支持的参数包括: clientidusernamepassword

MQTT 示例

让我们更详细地探讨使用 NGINX Plus 处理 MQTT 消息的好处以及相关的最佳实践。 请注意,我们在下面的示例中使用了端口 1883 和 8883。 端口 1883 是默认的不安全 MQTT 端口,而 8883 是默认的 SSL/TLS 加密端口。

MQTT 代理负载均衡

MQTT 设备的短暂性可能会导致客户端 IP 意外改变。 在将设备连接路由到正确的上游代理时,这可能会带来挑战。 随后,设备连接从一个上游代理移动到另一个上游代理可能会导致代理之间昂贵的同步操作,从而增加延迟和成本。

通过解析 MQTT CONNECT 消息中的clientid字段,NGINX 可以与上游服务代理建立粘性会话。 这是通过使用clientid作为哈希键来维护与后端代理服务的连接来实现的。

在此示例中,我们使用clientid作为令牌来代理 MQTT 设备数据,以便与三个上游代理建立粘性会话。 我们使用一致参数,以便如果上游服务器出现故障,其流量份额会均匀分布在剩余的服务器上,而不会影响这些服务器上已经建立的会话。

流 {     mqtt_preread on; 
   
    上游后端 {
        zone tcp_mem 64k;
        hash $mqtt_preread_clientid consistent;
  
        服务器 10.0.0.7:1883; # 上游 mqtt 代理 1
        服务器 10.0.0.8:1883; # 上游 mqtt 代理 2
        服务器 10.0.0.9:1883; # 上游 mqtt 代理 3 
    }
  
    服务器 {
         listen 1883;
         proxy_pass 后端;
       代理连接超时 1s;
    }
}

NGINX Plus 还可以解析 MQTT CONNECT 消息的用户名字段。 更多详细信息请参阅ngx_stream_mqtt_preread_module规范。 

SSL/TLS 终止

加密设备通信是确保数据机密性和防止中间人攻击的关键。 但是,TLS 握手、加密和解密可能会增加 MQTT 代理的资源负担。 为了解决这个问题,NGINX Plus 可以从代理(或代理集群)卸载数据加密,从而简化安全规则并允许代理专注于处理设备消息。 

在此示例中,我们展示了如何使用 NGINX 将 TLS 加密的 MQTT 流量从设备代理到后端代理。 ssl_session_cache指令定义了一个 5 兆字节的缓存,足以存储大约 20,000 个 SSL 会话。 NGINX 将在超时前五秒尝试连接代理代理,如proxy_connect_timeout指令所定义。

流 {     服务器 {
         监听 8883 ssl;
         ssl_certificate /etc/nginx/certs/tls-cert.crt;
         ssl_certificate_key /etc/nginx/certs/tls-key.key;
         ssl_session_cache 共享:SSL:5m;
         proxy_pass 10.0.0.8:1883;
         proxy_connect_timeout 5s;
    }
} 

客户端 ID 替换

出于安全原因,您可以选择不在 MQTT 代理的数据库中存储客户端可识别信息。 例如,设备可以发送序列号或其他敏感数据作为 MQTT CONNECT 消息的一部分。 通过将设备的标识符替换为从客户端收到的其他已知静态值,可以为每个尝试访问 NGINX Plus 代理服务器的设备建立一个备用唯一密钥。

在此示例中,我们从设备的客户端 SSL 证书中提取唯一标识符,并使用它来屏蔽其 MQTT 客户端 ID。客户端证书身份验证(双向 TLS)由ssl_verify_client指令控制。 当设置为 on 参数时,NGINX 确保客户端证书由受信任的证书颁发机构 (CA) 签名。 受信任的 CA 证书列表由ssl_client_certificate指令定义。 

流 {     mqtt on; 
  
    服务器 {
        监听 8883 ssl;
        ssl_certificate /etc/nginx/certs/tls-cert.crt;
        ssl_certificate_key /etc/nginx/certs/tls-key.key;
        ssl_client_certificate /etc/nginx/certs/client-ca.crt;
        ssl_session_cache shared:SSL:10m;
         ssl_verify_client on;
         proxy_pass 10.0.0.8:1883;
         proxy_connect_timeout 1s;
         
        mqtt_set_connect 客户端 ID $ssl_client_serial;
    }
}

客户端证书作为身份验证凭证

验证 MQTT 客户端的一种常见方法是使用存储在客户端证书中的数据作为用户名。 NGINX Plus 可以解析客户端证书并重写 MQTT 用户名字段,从而将此任务从后端代理卸载。 在下面的示例中,我们提取客户端证书的主题可分辨名称 (Subject DN) 并将其复制到 MQTT CONNECT 消息的用户名部分。

流 {     mqtt on; 
         服务器 {
        监听 8883 ssl;
        ssl_certificate /etc/nginx/certs/tls-cert.crt;
        ssl_certificate_key /etc/nginx/certs/tls-key.key;
        ssl_client_certificate /etc/nginx/certs/client-ca.crt;
        ssl_session_cache shared:SSL:10m;
         ssl_verify_client on;
         proxy_pass 10.0.0.8:1883;
         proxy_connect_timeout 1s;
         
        mqtt_set_connect 用户名 $ssl_client_s_dn;
    }
} 

有关 NGINX Plus MQTT CONNECT 消息重写的完整规范,请参阅ngx_stream_mqtt_filter_module规范

立即开始

NGINX Plus 中 MQTT 的未来发展可能包括解析其他 MQTT 消息类型,以及更深入地解析 CONNECT 消息以实现如下功能:

  • 附加身份验证和访问控制机制
  • 通过限制“健谈”客户的速率来保护经纪人
  • 消息遥测和连接指标

如果您是 NGINX Plus 新手,请注册30 天免费试用版以开始使用 MQTT。 我们也希望听到您对最关心的功能的反馈。 请在评论中告诉我们您的想法。


“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”