Skip to content

数据桥接 ​

桥接是一种连接多个 MQTT 消息中间件的策略,其特性与集群模式显著不同。在桥接模式下,节点之间不进行主题树或路由表的复制操作。桥接模式的核心职能包括:

  • 根据预定的规则,将消息转发至指定的桥接节点;
  • 对桥接节点上的特定主题进行订阅,并在接收到消息后在本地节点或集群内进行传递和转发。

MQTT over TCP 桥接 ​

本节将介绍 MQTT over TCP 数据桥接相关的配置参数。

配置示例 ​

hcl
bridges.mqtt.emqx1 = {
  server = "mqtt-tcp://127.0.0.1:1883"    # MQTT 服务器地址
  proto_ver = 4                           # MQTT 协议版本
  clientid = "bridge_client"              # 桥接的客户端 ID
  keepalive = "60s"                       # 桥接的保活间隔时间(s)
  clean_start = false                     # 清除会话
  username = "username"                   # 桥接用户名
  password = "passwd"                     # 桥接密码
  will = {                                # 遗嘱消息相关配置
  	topic = "will_topic"                  # Will 主题
  	qos = 1                               # Will QoS
  	retain = false                        # 是否应保留遗嘱消息
  	payload = "will_message"              # Will payload
  	properties = {                        # Will 消息属性
    	payload_format_indicator = 0
    	message_expiry_interval = 0
    	content_type = ""
    	response_topic = ""
    	correlation_data = ""
    	will_delay_interval = 0
    	user_property = {
      	key1 = "value1"
      	key2 = "value2"
    	}
  	}
  }
  ssl = {                                 # SSL 配置
    key_password = "yourpass"             # SSL 密钥密码
    keyfile = "/etc/certs/key.pem"        # SSL 密钥文件
    certfile = "/etc/certs/cert.pem"      # SSL 证书文件
    cacertfile = "/etc/certs/cacert.pem"  # SSL CA 证书文件
    verify_peer = true                    # 启用 SSL 对端证书验证

    sni = "broker.emqx.io"                # SSL Server Name Indication
  }
  
  forwards = [                            # 要转发到远端 MQTT 服务器的主题
    {
      remote_topic = "fwd/topic1"
      local_topic = "topic1"
      suffix = "/emqx"
    },
    {
      remote_topic = "fwd/topic2"
      local_topic = "topic2"
      prefix = "emqx/"
    }
  ]     
  subscription = [                        # 要从远端 MQTT 服务器订阅的主题
    {
      remote_topic = "cmd/topic1"
      local_topic = "topic3"
      qos = 1
      retain = 2                          # 重载标志位
    },
    {
      remote_topic = "cmd/topic2"
      local_topic = "topic4"
      qos = 2
    }
  ]
  max_parallel_processes = 2              # 最大并行进程数
  max_send_queue_len = 32                 # 消息发送队列的最大长度
  max_recv_queue_len = 128                # 消息接收队列的最大长度
}

通过以上配置,EMQX Edge 可以建立到远端 MQTT 服务器的 MQTT over TCP 桥接,并使用 Will Message 和 SSL 安全通信。

配置项 ​

  • bridges.mqtt.<name>:桥接名称。
  • server:桥接目标 Broker 的地址 URL。示例:
    • MQTT over TCP 桥接:mqtt-tcp://127.0.0.1:1883
    • 经 SSL 加密的 MQTT over TCP 桥接:tls+mqtt-tcp://127.0.0.1:8883
    • MQTT over QUIC 桥接:mqtt-quic://54.75.171.11:14567
  • proto_ver:指定 MQTT 协议版本:可选值:
    • 5:MQTT v5
    • 4:MQTT v3.1.1
    • 3: MQTT v3.1
  • clientid:桥接客户端 ID,默认 NULL 为自动生成随机 ID。
  • keepalive:保活间隔时间。
  • clean_start:清除会话。注意:有些 IoT 平台会要求该项设为 false。
  • username:登录用户名。
  • password:登录密码。
  • ssl:SSL/TLS 相关配置项:
    • key_password:TLS 私钥密码。
    • keyfile:TLS 私钥数据。
    • certfile:TLS Cert 证书数据。
    • cacertfile:TLS CA 证书数据。
    • verify_peer:指定是否验证远端 MQTT 服务器证书。
    • sni:指定 TLS 握手期间发送的 Server Name Indication(SNI)值,通常设置为远端 Broker 的主机名。
  • forwards:转发到远端 MQTT 服务器的主题数组,包括:
    • remote_topic:发布到远端 Broker 的主题。将 remote_topic 留空可保留原始主题。
    • local_topic:需要转发到远端 MQTT 服务器的本地主题。
    • qos:覆盖原始 PUBLISH 消息的 QoS 等级。该项可选。
    • suffix:添加到远端主题的后缀字符串。如果 remote_topic 为空,则添加到原始主题。
    • prefix:添加到远端主题的前缀字符串。如果 remote_topic 为空,则添加到原始主题。
  • subscription:需要从远程 MQTT 服务器订阅的主题对象数组:
    • remote_topic:用于订阅远端 Broker 的主题过滤器。
    • local_topic:用于主题映射。将 local_topic 留空可保留远端 Broker 消息中的原始主题。
    • qos:SUBSCRIBE 报文中的 QoS。该项必填。
    • retain:重载标志位。
    • retain_as_published:MQTT v5 可选特性,Retain As Published。
    • retain_handling:MQTT v5 可选特性,Retain Handling。
    • suffix:添加到本地主题的后缀字符串。如果 local_topic 为空,则添加到原始主题。
    • prefix:添加到本地主题的前缀字符串。如果 local_topic 为空,则添加到原始主题。
  • max_parallel_processes:接客户端并发数。
  • max_send_queue_len:最大发送队列长度。
  • max_recv_queue_len:最大接收队列长度。

MQTT 5 ​

如果选择使用 MQTT 5.0 协议(proto_ver = 5),EMQX Edge 还支持以下配置项:

连接相关:

配置项说明取值范围
conn_property.maximum_packet_size最大报文长度1 - 4294967295
conn_properties.receive_maximumQoS 1 和 QoS 2 消息的最大接收数量,仅在当前连接下有效。
如未配置,将使用默认值 65535。
1 - 65535
conn_properties.topic_alias_maximum主题别名最大长度0 - 65535
conn_properties.request_problem_information请求问题信息:

- 如设为 0,服务器仅可在 PUBLISH、CONNACK 或 DISCONNECT 包中插入问题信息。如违反该规则,客户端则将断开连接并返回协议错误信息。

- 如设为 1,则不限制包的类型。
0 或 1
conn_properties.request_response_information请求响应信息:

- 如设为 0,服务器禁止返回响应信息。

- 如设为 1,服务器可以在 CONNACK 包中返回响应信息。
0 或 1
conn_properties.session_expiry_interval会话过期间隔:

- 如设为 0,会话将在网络连接关闭后结束。

- 如设为 4294967295 (UINT_MAX),会话则永不过期。
0 - 4294967295
conn_properties.user_property用户属性键值对。Map[key(String) - value(String)]

订阅相关

配置项说明取值范围
sub_properties.identifier订阅标识符1 ~ 268,435,455
sub_properties.user_property用户属性Map[key(String) - value(String)]*

遗嘱消息 (Will Message) ​

遗嘱消息是 MQTT 为那些可能出现意外断线的设备提供的将 遗嘱 优雅地发送给第三方的能力,意外断线包括但不限于:

  • 因网络故障或网络波动,设备在保持连接周期内未能通讯,连接被服务端关闭
  • 设备意外掉电
  • 设备尝试进行不被允许的操作而被服务端关闭连接,例如订阅自身权限以外的主题等

相关配置项包括:

  • will.topic:指定应发布遗嘱消息的主题。

  • will.payload:指定遗嘱消息的有效 Payload,通常连接断开的通知消息。

  • will.qos:遗嘱消息的 QoS 等级。可以是 0(最多一次)、1(至少一次)或 2(恰好一次)。

  • will.retain:指定是否应保留遗嘱消息。如设置为 true,Broker 会存储遗嘱消息,并发送给后续订阅该主题的订阅者。

  • will.properties:

    • payload_format_indicator:指定遗嘱消息的 Payload 格式。可选值:0 或 1。0 表示未指定的字节流,1表示 UTF-8 字符串。
    • message_expiry_interval:指定遗嘱消息的保留时间段(单位:秒)。如未配置,消息将永不过期。
    • content_type:指定遗嘱消息 Payload 的内容类型,用于解释 Payload 中包含的数据。
    • response_topic:指定遗嘱消息的响应主题。其他客户端可以使用此主题发送对遗嘱消息的响应。
    • correlation_data:指定用于将响应与遗嘱消息相关联的二进制数据。
    • will_delay_interval:指定客户端非正常断开连接与代理发布遗嘱消息之间的延迟。单位:秒。注意:默认值 0 表示没有延迟。
    • user_property:用户定义键值对,用户可按照 key1 = value1 格式发送其他自定义数据。

MQTT over QUIC 桥接 ​

本节将介绍 MQTT over QUIC 数据桥接相关的配置参数。QUIC 最初由 Google 开发,后来被互联网工程任务组(IETF)采纳为全球标准。它是一种新的传输协议,提供更快的连接建立速度。通过 MQTT over QUIC 数据桥接,我们可以充分发挥 QUIC 协议在 IoT 场景中的优势。

配置示例 ​

hcl
bridges.mqtt.emqx1 = {
  server = "mqtt-quic://127.0.0.1:14567"  # MQTT 服务器地址
  proto_ver = 4                           # MQTT 协议版本
  clientid = "bridge_client"              # 桥接的客户端 ID
  keepalive = "60s"                       # 桥接的保活间隔时间(s)
  clean_start = false                     # 清除会话
  username = "username"                   # 桥接用户名
  password = "passwd"                     # 桥接密码
  quic_keepalive = "120s"                 # 使用 QUIC 桥接的 ping 间隔
  quic_idle_timeout = "120s"              # 使用 QUIC 桥接的连接最大过期时间
  quic_discon_timeout = "20s"             # QUIC 桥接等待连接 ACK 最大时间
  quic_handshake_timeout = "60s"          # QUIC 握手最大超时时间
  quic_send_idle_timeout = "2s"           # QUIC 传输层重置拥塞控制算法的等待超时时间
  quic_initial_rtt_ms = "800ms"           # 初始 RTT 估计时间
  quic_max_ack_delay_ms = "100ms"         # 发送 ACK 之前接收数据后等待时长
  hybrid_bridging = false                 # 混合桥接模式开关
  quic_multi_stream = false               # 多流传输开关
  quic_qos_priority = true                # 高优先级发送 QOS 1 或 2 的消息
  quic_0rtt = true                        # 0RTT 开关,用于快速重新建立连接
  forwards = [                            # 要转发到远端 MQTT 服务器的主题
    {
      remote_topic = "fwd/topic1"
      local_topic = "topic1"
      qos = 1
    },
    {
      remote_topic = "fwd/topic2"
      local_topic = "topic2"
      qos = 2
    }
  ]     
  subscription = [                        # 要从远端 MQTT 服务器订阅的主题
    {
      remote_topic = "cmd/topic1"
      local_topic = "topic3"
      qos = 1
    },
    {
      remote_topic = "cmd/topic2"
      local_topic = "topic4"
      qos = 2
    }
  ]
  max_parallel_processes = 2              # 最大并行进程数
  max_send_queue_len = 32                 # 消息发送队列的最大长度
  max_recv_queue_len = 128                # 消息接收队列的最大长度
}

配置项 ​

本部分重点介绍 MQTT over QUIC 桥接相关的配置项,其他配置项可参考 MQTT over TCP 桥接。

  • Server:桥接的 MQTT 服务器地址,例如 mqtt-quic://54.75.171.11:14567
  • quic_keepalive:QUIC 传输层保活时间,缺省为 120 秒。
  • quic_idle_timeout:QUIC 连接最大过期时间,超时后,连接将被断开。0 表示永不超时,缺省为 120 秒。
  • quic_discon_timeout:QUIC 等待连接 ACK 最大时间 ,缺省为 20s。
  • quic_handshake_timeout:QUIC 握手最大超时时间,缺省为 60 秒。
  • quic_send_idle_timeout:传输层重置拥塞控制算法的等待超时时间,缺省为 60 秒。
  • quic_initial_rtt_ms:初始 RTT 估计时间,单位:ms,缺省为 800 ms。
  • quic_max_ack_delay_ms:发送 ACK 之前接收数据后等待时长,缺省为 100 ms。
  • hybrid_bridging:确认是否开启混合桥接模式;缺省为 false。
  • hybrid_servers:确认混合桥接URLs;缺省为 []。
  • quic_multi_stream:确认是否开始多流传输,本功能目前正在进一步验证中,不建议开启。缺省为 false。
  • quic_qos_priority:确认高优先级发送 QOS 1 或 2 的消息,QoS 0 消息的优先级不变。缺省为 true。
  • quic_0rtt:确认是否开启 0RTT QUIC 协议特性,用于快速重新建立连接。缺省为 true。

TIP

MQTT over QUIC 桥接暂不支持 SSL 相关配置。

MQTT 桥接缓存 ​

EMQX Edge 允许在 nanomq.conf 文件中配置多个 MQTT 数据桥接。每个桥接实例都可以通过名称唯一标识。除各桥接自身配置外,EMQX Edge 还提供共享的 cache 组件,可配置一次并被多个桥接实例使用。

该缓存功能尤其适用于桥接负载较高,或远端 Broker 暂时不可用时临时保存 QoS 消息。

配置示例 ​

hcl
## 第一个桥接客户端
bridges.mqtt.emqx1 {
  retry_qos_0 = true
  ......
  resend_interval = 5000    # Resend interval (ms)
  resend_wait = 3000
  cancel_timeout  = 10000
}

## 第二个桥接客户端
bridges.mqtt.emqx2 {
  retry_qos_0 = false
  ......
  resend_interval = 5000    # Resend interval (ms)
  resend_wait = 3000
  cancel_timeout  = 10000
}

bridges.mqtt.cache {
    disk_cache_size = 102400   # Max message limitation for caching
    mounted_file_path="/tmp/"  # Mounted file path
    flush_mem_threshold = 100  # The threshold of flushing messages to flash
}

配置项 ​

  • retry_qos_0:指定是否缓存 QoS 0 消息。设置为 false 可为 QoS 1 和 QoS 2 消息保留磁盘空间,适用于需要优先级处理的场景。
  • disk_cache_size:指定 MQTT 桥接中可以缓存的消息的最大数量。0 表示不生效。
  • mounted_file_path:指定 MQTT 桥接缓存文件的挂载路径。
  • flush_mem_threshold:指定刷新消息到缓存文件的阈值。当消息数量达到阈值时,就会被刷新到缓存文件中。
  • resend_interval:指定消息重发间隔,单位为毫秒。该配置仅对桥接生效。它是每个桥接连接的定时器,也负责发送 PINGREQ、重发 SQLite 中缓存的消息和健康检查,请谨慎设置。
    • 默认值:5000 ms。
  • resend_wait:指定消息发布后等待多久再重发,单位为毫秒。仅对桥接生效。
    • 默认值:3000 ms。
  • cancel_timeout:指定取消 QoS ACK 动作前的最长等待时间,单位为毫秒。仅对桥接生效。动作取消后,该消息不会继续重试。
    • 默认值:8000 ms。

QoS 消息处理说明

取消 QoS 消息并不意味着消息丢失,只表示客户端已停止等待远端 Broker 的 ACK。

在 0.22.4 之前的版本中没有该机制。如果桥接遇到繁忙的 I/O 线程(aio),可能会看到如下消息: bridging to xxxxx aio busy! msg lost! Ctx: xx 如果远端 Broker 一直没有响应,该消息会持续卡住。

为避免这种情况,引入了 cancel_timeout 作为安全窗口,避免无限期阻塞。它定义 QoS 消息在从重试队列丢弃前等待 ACK 的时间。

保证 QoS 消息重试(调优示例) ​

对于桥接中的 QoS 消息重试保证,可参考以下配置:

hcl
## 第一个桥接客户端
bridges.mqtt.emqx1 {
  ......
  keepalive = 30s           # Taking 30s keepalive as context
  max_send_queue_len = 512  # Give the in-flight window enough space for caching messages
  max_recv_queue_len = 512  # Give the in-flight window enough space for caching messages
  resend_interval = 5000    # Retry QoS messages every 5 seconds if no other action is blocking.
                            # Set this to at least 1/2 or 1/4 of the keepalive interval.
  resend_wait = 3000        # Wait before resending messages after they are published.
                            # Set this longer than keepalive to avoid duplicate QoS messages.
  cancel_timeout  = 10000   # Maximum time before canceling the ACK action.
                            # This is also the time window reserved for each QoS message.
                            # (cancel_timeout - resend_wait) / resend_wait > 1: retry at least once.
}

调优建议 ​

  • QoS 消息会先进入飞行窗口(max_send_queue_len)。如果网络拥塞,它们会静默等待,直到有可用空间。

  • 只有当飞行窗口已满且新的 QoS 消息持续到达时,消息才会被丢弃。

  • 每条 QoS 消息的实际超时时间为:cancel_timeout - resend_wait。 将该值除以 resend_interval,可估算可能的重试次数。

    例如:(10000 - 3000) / 5000 ≈ 1.4 表示至少重试 1 次。

达到超时时间后,消息会从 QoS 等待队列中移除(不是从飞行窗口中移除),并且不会再继续重发。

EMQX Edge 使用 SQLite 提供缓存功能。有关配置详情,请参见 Cache。