异步编程中,为什么一些类库不提供限制队列长度的方式

2023-05-10 23:20:23 +08:00
 546L5LiK6ZOt

在 java 中,利用 nio 来发起网络请求,基本步骤是:

  1. 业务线程把请求写到一个内存队列,然后往相关的 channel 注册一个写事件
  2. select 方法是单独运行在另一个线程,当 channel 可写时,就会从内存队列中获取要发送的数据,然后写进 channel

java11 的 httpclient ,以及 apache 的 httpclient ,都提供异步请求的方式,底层也是利用 nio 。但是没有提供限制内存队列的方式。也就是说,如果网络抖动,短时间等不到“可写”事件的发生,加上频繁发起网络请求,那么内存队列有可能会占用很大内存。本质上这是生产者和消费者模型,两个线程交互必须要考虑内存队列大小的,不然生产速度远大于消费速度就 GG 了。stackoverflow 上有个回答是利用 semaphor. 但是 semaphor 是在请求结束了才 release ,性能最好的方式应该是请求数据写进 channel 就 release 。

相反,netty 可以限制内存队列,并且还能配置拒绝策略(不愧是号称 java 最好的网络 io 库)。不理解 java 和 apache 官方的类库为啥不考虑这一点。

再扩展一下,像 go 这种支持协程的语言,并发发起网络请求都是起一个协程,底层肯定也是有一个内存队列缓存请求数据的,貌似也没有限制队列长度的方式(我对 go 不熟悉,不知道理解对不对)。

难道 channel 大概率是可写的,内存队列堆积的可能性非常小,不需要考虑?

933 次点击
所在节点    问与答
4 条回复
hankai17
2023-05-10 23:44:03 +08:00
没看过 netty httpclient apache 的源码 看过几个网络库源码
猜一下
1. 队列长度应该有限制 即使没有 也会受其它比如并发 打开的文件个数限制
2. 除此外 队列应该也有超时"淘汰"机制
byte10
2023-05-11 10:04:43 +08:00
没有看到 Netty 的源码,倒是自己使用 netty 写过一下 http 服务端和 http 客户端

NIO 发起网络,一般就是一个请求一个链接 channel 吧,它提供了简单的通讯框架。一般是上一层来维护连接池,比如基于 netty 的 http 客户端,有可能可以设置 http 连接池。如果没有设置的话,那么就是默认一次请求一个连接 channel 。但如果共享链接池的话,就会从任务队列拿数据进行发送,这个队列应该是 http 客户端来维护。同理其他的协议也是类似,比如 redis 协议等。

另外 NIO 本来存在背压的问题,还是要从业务层来规避下,限流下。
ql562482472
2023-05-11 10:26:09 +08:00
tcp 缓冲区怎么是无限的?
documentzhangx66
2023-05-12 04:16:48 +08:00
如果用 C++的思维去理解,就很容易解释了。

这是因为,对于高性能组件来说,限制长度这个功能,对性能的冲击非常大,因为它需要检测与同步,在多线程甚至多 CPU 情况下,这种代价会加剧变大。很多网络组件,为了高性能,甚至改用不精准的定长 ringbuffer 的满载时首尾位置判断,来代替更精准的实时长度。

你可以简单找个大内存服务器,不做 free 或 delete ,来比较一下 queue 或 list 入队时,有无长度检测或容量检测,对性能的影响。特别是多 CPU 多线程的情况。

如果计算机性能,能够得到大幅度提高,那么实时获取长度,对程序员的体验来说,当然更好。但那个时候,对性能又会有更高的要求。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/939049

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX