首页 > 解决方案 > Java NIO - 消息的累积

问题描述

我正在编写一个使用 NIO 套接字的 Java 应用程序。它由 3 台服务器和一组客户端组成。客户端可以与服务器通信,服务器可以与客户端和其他服务器通信。

服务器到服务器和客户端到服务器发送Message序列化到byte[]数组的 s。每个的第一个字节Message包含消息的大小,自然保证每个消息不包含超过127 (2^8 -1)字节。您可以将服务器和客户端的消息发送视为循环操作:

Message msg = new Message()
while (true) {
    sendMessage(msg, server or client)
    receiveMessage()
}

然后,实现使用ByteBuffer. 自然,就像在任何 Java NIO 实现中一样,每个服务器都会执行selector.select()然后检索SelectionKeys 以查看是否需要read处理 ing(调用handleRead()方法)、writeing(调用handleWrite())或accepting(调用handleAccept())。所有handleXX方法都采取有限数量的步骤,并且不会阻塞等待其他任何事情。

在检索handleRead()特定的数据()时key,我只是将数据存储在特定的地图中Map<SelectionKey, List<byte[]>> readDataForKey;然后我遍历列表并提取所有已收到的消息。

但是,我注意到有时在输入handleRead某些消息时,key会有数千条消息等待处理。我无法弄清楚为什么会这样?我希望handleRead看到几条消息,就是这样。

事实上,有数千条消息在被处理之前就已经累积起来。这意味着什么?这是否意味着我handleReadhandleWriteNIO 实现的某些其他部分花费了太长时间并且底层缓冲区已满?这是否意味着有时我会得到一个 GC(大约 10 毫秒),同时缓冲区已满?这是否意味着我的代码可能很慢handleRead,因此消息会累积?

这么多消息堆积正常吗?

标签: javaserverclientnio

解决方案


你的消息非常小。所以发送和接收缓冲区可以包含很多消息。如果您没有明确指定发送和接收缓冲区的大小,TCP 堆栈将尽最大努力优化大小,以便通过您的网络进行有效传输。虽然大多数 Linux 发行版的默认大小约为 128 KB,但最大大小可能是数兆字节(例如,在具有非常高延迟的网络上)。因此,单个 handleRead 可以轻松查看成百上千条消息. TCP 尝试尽可能好地使用您的网络带宽。在接收缓冲区中看到大量消息并不意味着您的接收器已超载。TCP 甚至可以避免您的接收器过载。衡量您的系统是否“过载”的唯一方法是衡量发送消息所需的时间(例如,在发送消息之前对消息进行排队并测量其大小)

在某些情况下手动优化缓冲区大小是有意义的(例如具有非常高延迟的网络),但大多数时候 TCP 堆栈在这里做得很好。在极少数情况下,禁用 Nagle 的算法(例如 Telnet 和 SSH)以最大限度地减少对用户输入的反应时间是有意义的,但大多数情况下,绝对不需要手动干预。让 TCP 完成它的工作,即优化有效传输并避免接收器过载。


推荐阅读