How to set the suitable 「message queue」 length?

refer to How to set the suitable 「message queue」 length? · Issue #13983 · emqx/emqx · GitHub

类似
message_queue_too_long 异常导致服务端连接断开

其他相关 issue:
EMQ消息队列配置问题

如果你想问的是如何避免 message_queue_too_long 异常导致服务端连接断开 中提到的客户端消息邮箱积压过大被 emqx 杀死问题:

你可以通过 zone.external.force_shutdown_policy = 10000|64MB (emqx 4.x) 或 force_shutdown.max_mailbox_size = 1000 (emqx 5.x) 来更改默认的邮箱上限。

但基本的原则是不要修改他,默认值 10000 (emqx 4.x) 或 1000 (emqx 5.x) 已经足够大,一旦因为客户端进程超过这个上限而被系统杀死,意味着系统出现了瓶颈,应该首先调查问题出现的原因,增大这个参数无法解决问题。

emqx remote_console
然后在 console 内部执行 observer_cli:start().,然后输入mq 按照邮箱长度排序。
在问题发生时将能看到某个进程的邮箱非常大,按回车或者进程序号查看进程详情,特别是 Current Stack Trace (C)。

另外,这里有一篇文章描述了如何应对单个订阅者产生的瓶颈:https://juejin.cn/post/7390957334419816500

感谢回复。

事实上,force_shutdown.max_mailbox_size 参数调大之后同样存在会话被杀死的情况,所以系统的瓶颈应该不在 mailbox 这里。

请允许我再复述一遍场景:
一些客户端离线后,以 ? w/s 的速度对这些客户端订阅的 topic 进行写入,message queue 开始积压。
根据多次测试的统计,10 个客户端共积压约 10,000,000 条数据时,客户端会话会被强制清除。

根据一般认知,message queue 的长度与服务器的内存大小强相关,调高内存后应相应容纳更多 message,但事实似乎并不如此。

通过查阅配置手册,试图调整了force_shutdown.max_heap_size = 2048MB参数,由于我并没有 Erlang 相关的知识,我猜测 2048 MB 实际上是一个「轻量级」进程可能无法真正申请到的内存大小。无论如何,每个 Erlang 进程获取到的参数一定比原先要更大些。
调整后,message queue 最大容量(指测试中发现会话被杀死前 message queue 的长度)有所上升,但我仍为此感到困惑。


文章 EMQX 避坑 - 订阅和共享订阅 中提到的 TCP 缓冲区拥塞和进程被强制杀死后的日志非常有用,为我的场景提供了很多有用的参考。

首先,我在 8 核心的服务器上使用了 1 个共享订阅用以测试,共 10 个客户端用以消费消息(实际上并未消费,因为他们一直处于断开状态,目的是为了测试 message queue 的长度极限)。**根据文章中提供的模型,写入速度过高想必对即使 10 个进程来说依然是不小的负担,再加上单个客户端 message queue 在超过 800,000 长度后出入队和内存寻址带来的压力,会不断趋近进程的性能边界。**之后,进程被杀死。

所以我将尝试提高 CPU 核心数量,同时提高客户端数量,观察 message queue 的总容量是否得到提升。

其他:
有关参数 force_shutdown.enable 如果置为 false,即关闭强制杀死进程的功能(根据参数描述),在正常运行的情况下(写入速度、客户端数量、共享订阅数量均视为使服务器低负载的值),是否也会降低 EMQX 的稳定性?如果可以,请就这个参数提供更多信息,十分感谢。

再次感谢回复,有关后续的测试进展及问题我会继续在此 thread 中回复。

The force_shutdown configuration is just to protect the emqx from out of memory.

If you disable the force_shutdown configuration or set it to a very large value, EMQX may run out of available memory and crash when a single MQTT process uses too much memory.

If you are using persistent sessions (clean start = false) and the client is offline, the messages should be buffered in the session’s message queue, which is controlled by mqtt.max_mqueue_len (default value is 1000). Once the queue is full, it will start discarding old messages from the queue. In this case, the process should not be killed unless you have also modified mqtt.max_mqueue_len.

QoS0 messages will not be buffered in the session’s message queue by default, if you blocked the network between MQTT clients and emqx and send QoS0 messages, then the mailbox of the processes would be grow.