Skip to content

飞行窗口和消息队列 ​

EMQX 使用飞行窗口和消息队列提高消息吞吐量,并减小网络波动对消息投递的影响。飞行窗口和消息队列均不由所有客户端全局共享,EMQX 会为每个客户端连接分别维护。

  • 飞行窗口:保存已发送但尚未确认的 QoS 1 和 QoS 2 消息,直至 EMQX 收到相应的确认报文。EMQX 允许多个消息同时在途,max_inflight 限制在途消息的数量。对于 MQTT 5.0 客户端,EMQX 可同时发送且尚未收到确认的 QoS 1 和 QoS 2 消息数量,取 max_inflight 与客户端声明的 Receive Maximum 中的较小值。
  • 消息队列:缓存内存会话中无法立即投递的消息。内存会话的会话状态保存在 EMQX 节点内存中。

以下情况下,消息可能进入消息队列:

  • 客户端断开连接,但会话仍保留。客户端重新连接后,EMQX 会投递队列中的消息。
  • QoS 1 和 QoS 2 消息正在等待飞行窗口槽位或投递配额。
  • 连接发生拥塞。

启用 mqueue_store_qos0 后,EMQX 也可能在客户端离线、连接拥塞或需要保持投递顺序时,将 QoS 0 消息存入消息队列。将该配置设置为 false 后,EMQX 不会缓存客户端离线期间到达的 QoS 0 消息。

消息队列的投递行为 ​

EMQX 会根据连接是否拥塞采用不同的消息队列投递行为:

  • 连接未拥塞时,飞行窗口中的消息数量达到 max_inflight 限制后,EMQX 会暂停投递后续 QoS 1 和 QoS 2 消息,直至出现可用槽位。QoS 0 消息不会进入飞行窗口,因此即使其前面存在等待槽位的 QoS 1 或 QoS 2 消息,EMQX 仍可继续投递队列中的 QoS 0 消息。
  • 连接拥塞时,可用飞行窗口槽位会限制 EMQX 响应客户端确认报文时一次出队的消息数量,不区分消息的 QoS 等级。连接恢复或飞行窗口产生更多可用槽位后,EMQX 会继续投递队列中的消息。

EMQX 会保持相同主题和 QoS 等级的消息投递顺序,但不保证不同 QoS 等级的消息严格按先入先出顺序投递。启用主题优先级后,EMQX 会按主题优先级调度队列中的消息。即使更低优先级的队列中仍有 QoS 0 消息,当前优先级队列中没有可投递的 QoS 0 消息时,本轮出队也会停止。

当消息队列达到 max_mqueue_len 限制时,EMQX 会淘汰最早入队的 QoS 0 消息。如果队列中没有 QoS 0 消息,EMQX 会淘汰最早入队的其他消息。启用主题优先级后,该限制和淘汰策略会分别应用于各优先级队列。当短时间内有大量 QoS 0 消息进入队列时,该策略有助于继续投递 QoS 1 和 QoS 2 消息。

飞行窗口与 Receive Maximum ​

MQTT v5.0 协议为 CONNECT 报文新增了一个 Receive Maximum 的属性,官方对它的解释是:

客户端使用此值限制客户端愿意同时处理的 QoS 为 1 和 QoS 为 2 的发布消息最大数量。没有机制可以限制服务端试图发送的 QoS 为 0 的发布消息 。

也就是说,服务端可以在等待确认时使用不同的报文标识符向客户端发送后续的 PUBLISH 报文,直到未被确认的报文数量到达 Receive Maximum 限制。

不难看出,Receive Maximum 其实与 EMQX 中的飞行窗口机制如出一辙,只是在 MQTT v5.0 协议发布前,EMQX 就已经对接入的 MQTT 客户端提供了这一功能。现在,使用 MQTT v5.0 协议的客户端将按照 Receive Maximum 的规范来设置飞行窗口的最大长度,而更低版本 MQTT 协议的客户端则依然按照配置来设置。

然而,EMQX 并不一定会授予 CONNECT 数据包中请求的 Receive Maximum 值。相反,在 CONNACK 数据包中授予的 Receive Maximum 受 mqtt.max_inflight 配置的限制。

配置项 ​

配置项类型可选值默认值描述
mqtt.max_inflight整数[1, 65535]32EMQX 可同时投递且尚未收到确认的 QoS 1 和 QoS 2 消息的最大数量。对于 MQTT 5.0 客户端,以较小的 Receive Maximum 为准。
mqtt.max_mqueue_len整数[0, ∞)1000内存会话消息队列中的最大消息数量。0 表示无限制。
mqtt.mqueue_store_qos0枚举true, falsetrue客户端断开连接或连接拥塞时,EMQX 是否将 QoS 0 消息存入内存会话的消息队列。EMQX 也可能为保持投递顺序而将 QoS 0 消息入队。