101
Narcissu5 2022-04-19 15:14:34 +08:00
异步不纯粹就纯粹不异步,java 这种提供一部分接口然后就没有了的方案缺失很鸡肋。尤其是 jdbc 没有异步接口是个绕不过去的问题。当然你可以开线程池,最后就发现大量线程还是 block 在数据库上
|
102
jeesk OP @lmshl 大部分应用都是 io , 只有交易撮合, 广告竞价,算法之类的应用是 cpu 应用。
|
103
byte10 2022-04-19 18:41:34 +08:00
@Jekins 可以看看那个 B 战-小巫老师,视频:NIO 场景和实战,有很多场景和代码验证。还有一个 NIO 服务的绝对优势,看完你就知道小白的问题在哪里了。里面还有一个多线程的讲解,也有代码验证 《最大线程数计算公式》的例子。大多数人都不知道怎么去理解 NIO ,他们以为 用上 tomcat 8.x 后的就以为用上了 NIO 了,实际上还差一些。
@xxfye 正确,回答正确。tomcat NIO 要开 200 个线程,那是因为业务层“一般”一般一般依然使用阻塞模型,如果使用异步模型,那么只要少量的线程即可,但是需要使用者去主动设置。 关于网上说的:bio 上下文竞争比 nio 强烈 。因为 NIO 一般只需要设置等于 cpu 核心数即可,BIO 需要设置大量的线程,所以结果是:bio 上下文竞争比 nio 强烈。因果关系没搞明白吗? 如果你想测试多线程开销到底有多大的影响,我代码中有给出例子,你可以使用 TestMaxThreadNum.calculationCpuTime() 去验证,第一次设置 cpu 核心数量,第二次设置 2000 个线程,你就会发现 2000 大量的线程 跑起来没有线程数量少的快( cpu 密集型情况下)。所以 ”线程数量多“ 的上下文竞争 会比 ”线程数量少“ 的厉害,性能会有所下降。 另外 BIO 上下文竞争是否会比 NIO 厉害,这里要区分情况,如果 BIO 线程中经常出现 IO 等待,那么不会有特别多的线程竞争。只有在 IO 时间短,且存在大量的线程的情况下,才会出现这样的情况。要减少这种情况,就需要在业务 IO 时间短的时候,使用少量线程,这不好设置和判断。而 NIO 则可以无视 IO 时间,我在视频中有讲过。 能救一个算一个。 |
104
sampeng 2022-04-19 18:52:13 +08:00
我觉得有个逻辑很多人是不知道的。。都是盲目开线程数。也不管是不是 nio 还是 bio 模型。
另外,其实在正常情况,只要你的接口足够快,哪怕 2 个线程都够用。 |
105
lmshl 2022-04-19 19:40:04 +08:00 1
大多数人都是先把线程池用💩了,再学美团动态线程池💩上雕花,并不知道一开始就把事情做对,反而去质疑教科书是不是写错了。
|
106
statumer 2022-04-19 20:02:29 +08:00
再说个有意思的事,OP 提到的这个回答我找了下原地址
为什么 nio 效率会比 bio 高? - 锭阳的回答 - 知乎 https://www.zhihu.com/question/59356897/answer/165104063 也就 15 个人点赞,排名也很靠后。并没有很多小白点赞嘛。 [Imgur]( https://imgur.com/sGShzm3) |
107
rahuahua 2022-04-19 20:32:07 +08:00
1. 使用 BIO 上下文切换厉害, 如果是相同 4 核 cpu , 无论我是用 bio 还是 nio ,都用 200 个线程, 这个时候对 cpu 的竞争到底有多剧烈? 我个人觉得差不了多少。 所以这个说法是错的。
都用 200 个线程...... |
109
jeesk OP @lmshl 压榨? 有测试代码吗? 还是你随口说的? 我测试过 webflux 和 servlet 差距并不大。 可以看看我回复里面的压测文章, 发在简书里面的。
|
111
jeesk OP @rahuahua 我是说假如。 并不是真的。 我们生产有些应用线程直接使用的核数。 因为是 cpu 密集应用。
|
113
maximum2012 2022-04-19 21:51:31 +08:00
@seagull7558 他不是翻译的核心力量,他基本上就是打杂的
|
114
jeesk OP tomcat 的 nio 并不是全部是非阻塞的,分为 4 部分, 读请求头,读 request body ,写响应头和 body , 等待下一个连接,只有读 request body 和写请求头 body 体都是阻塞的。
|
115
lmshl 2022-04-19 21:57:56 +08:00 1
@jeesk 有做过社区分享,数据是 Prometheus 实时采集的 3 Pod CPU 合计,TPS 也是来自 Ingress 和 服务内暴露的。
我的应用是全异步,包括 HTTP 和 RPC 调用,数据库访问走 hikaricp JDBC 链接池,但技术栈是 Scala 这边的,Akka Stream 混合 ZIO 纤程调度,线程池仅有 N 个 fiber scheduler + 2N 个 Blocking 。 |
116
lmshl 2022-04-19 22:01:59 +08:00
Scala 生态里的 HTTP Body 读写都是纯异步的😏
|
117
chihiro2014 2022-04-19 22:57:39 +08:00
@jeesk webflux 只是更抗压而已,个人感觉它的背压机制,只是一种限流优化而已。国外 Josh Long 也说过,其实 Webflux 只是一种对多线程的更好封装=。=
|
118
zeni123 2022-04-20 06:08:07 +08:00 via iPhone
都用 200 个线程池 。。。
|
119
zeni123 2022-04-20 06:09:03 +08:00 via iPhone
就好像在问一公斤铁 一公斤棉那个重
|
120
defage 2022-04-20 08:44:48 +08:00
lz 不觉得在说“也差不多”,“差不大”的词的时候非常不严谨么。如果你拿普通 crud ,只是做完能跑起来,用这些没问题的。
本来就在论严谨的问题,放在当下内存动不动就是 32G 的时代,当然可以说都差不多了咯。 |
122
rahuahua 2022-04-20 09:32:58 +08:00
楼主可能也没经历过所谓的高并发,大牛搞出 NIO ,各种协程模型并不是闲的蛋疼,是真的因为需要,早些年还在讨论 C10K , 现在都在讨论 C1000K 是技术在进步。 不要觉得别人都是小白,这种心态不好
|
123
nothingistrue 2022-04-20 09:43:58 +08:00
发现楼上有些人是 “nio 优于 bio”,或者 “nio 要替代 bio” 的想法,这是不正确的。NIO 异步非阻塞,BIO 同步阻塞,这俩是应对不同场景的,不是不考虑场景就无脑用一个。如果是客户端应用读取一个文件准备编辑的场景,用 NIO 就是个沙雕(这里 UI 那里可以将读取文件的过程作为子过程异步调用,但读取文件这个过程,单线程阻塞是最优解)。
还有异步同步也是要分场景的,并且异步本身,也是分为全异步的 callback 模式跟异步同步结合的 async/await 模式。无脑异步,或者无脑 callback ,也是沙雕行为。 楼主是上面看法的反方,是错的,但不代表上面看法的正方就是对的。楼主的标题说的是很正确的,互联网上的很多知识都会带入非黑即白的世界观,这才是最大的误导。 |
124
SunFarrell 2022-04-20 09:45:57 +08:00
所以我们都看开发经验,技术类的文章都知道是花瓶,毕竟国内技术人员写技术文章表达能力不够严谨
|
125
bugFactory 2022-04-20 09:50:42 +08:00
@MoYi123 不用
|
126
bugFactory 2022-04-20 09:51:05 +08:00
@Jekins 你的问题加机器就可以解决
|
127
yazinnnn 2022-04-20 10:13:02 +08:00
楼主可以把测试的代码或者项目贴出来吗?想看看怎么写的
|
128
akira 2022-04-20 11:17:33 +08:00
每个人的认知都是不一样的,发出来的内容自然有可能会有偏差。 相比较之下,教科书的基础内容, 大牛的文章 出问题的概率就小很多,可信度较高,最多就是可能有过时。
总而言之,言而总之,看到一个东西,要自己思考 |
129
lmshl 2022-04-20 11:35:02 +08:00
@nothingistrue nio 确实完全优于 bio ,完全替代这毋庸置疑。2022 年的今天谁在 UI 线程读取文件?十年前都不这么做了好吧。
如果你真的基础扎实的话,你应该知道 memory hierarchy 中 CPU >> Memory >>> Disk ,即使是计算密集型应用,异步流式文件加载也可以尽可能的让计算过程提前开始,最简单的例子就是“读取 CSV 文件,统计数据”,省下来的 CPU 时间可以留给计算过程,让总计算时间缩的更短。 |
132
jeesk OP @nothingistrue nio 并不是异步的。只是非阻塞而已。 真正的异步是 aio 。
|
133
jeesk OP @yazinnnn https://www.jianshu.com/p/77e8b64ab710 测试的数据在这里, 不过没有内存的图而已。
|
134
jeesk OP @rahuahua 我说我们生产是有一部分是密集应用。 我什么时候说过用 cpu 密集应用测试过? 又来倒打一把?
|
135
jeesk OP @lmshl 你说得不对, 阻塞会线程让出 cpu 的。ui 线程不可能做耗时的操作的,这样 ui 线程会卡死。 异步处理就行了。 用这个例子证明 nio 和 bio 不太好。
|
136
lmshl 2022-04-20 12:21:29 +08:00
|
139
lmshl 2022-04-20 12:56:44 +08:00
@jeesk 但浏览器的 JS 是单线程的,交互是不分开的。Android/Winform/WPF 你也可以用 kotlin flow API ,和 C# async/await ,并没有什么不同。过去开新线程发信号,走 delegate ,今天纯异步 fiber
|
142
nothingistrue 2022-04-20 14:01:32 +08:00
|
143
nothingistrue 2022-04-20 14:03:40 +08:00
|
144
liuhan907 2022-04-20 14:10:45 +08:00
@nothingistrue 异步和同步和你用什么处理没关系。准备读取数据的时候数据已经复制到用户缓冲就是异步,否则是同步。线程池那些都是后话。
|
146
lmshl 2022-04-20 15:07:20 +08:00
@nothingistrue 还真都用异步 /nio 去读区的,Android 有 kotlin flow ,compose-jb 给搬到桌面端来了,无非就是多个 suspend / await 的事。而且我 Electron 压根就不可能用同步方法去读文件好吧,直接把 UI 卡白屏了,谁不是写个异步 /nio 读取呢。
只要你习惯了这事,就跟呼吸一样自然,说白了还是写的少了。 |
147
nothingistrue 2022-04-20 15:11:06 +08:00
@lmshl 你把响应式处理 /流式处理,跟 NIO 搞混了吧。
|
148
Joker123456789 2022-04-20 16:28:35 +08:00 1
我觉得你需要补充一下 ,TCP, HTTP 协议的相关知识。 搞清楚,线程是在什么时候开的,什么时候结束的。
我用大白话说一下吧: BIO 是打电话,如果同时很多人打进来 你就需要很多的手机 来处理。 并且每个电话接通后 你都必须完整的处理成功后才能挂断( BIO 就是必须处理完成才能挂断,千万别跟我杠 你可以先挂断 去接别的电话,因为这是不可以的), 你自己脑补一下,电话多了以后,接电话的人有多崩溃。 NIO 是发信息,如果同时很多人找你,你只是会收到很多信息而已,一部手机就可以处理了,而且你可以自己阅读信息的内容,来决定 先处理哪个,后处理哪个。 这么说,你应该有点概念了吧? 我再详细一点: BIO, 电话接通后,那头跟你说:我这边有一份文件,我念给你听哦, 然后 你拿起纸笔,一边听,一边写, 在他念完之前 你都必须 一直听着,即使信号不好 导致断断续续的,即使他念了几句,跑去上厕所了,又或者 他故意 一段一段的念给你听,每段之间 都要去上一次厕所,或者吃一次零食。 你都必须 老老实实的 拿着电话在这等,不能处理别的, 也就是说你一直被占线了。 NIO ,那头 给你发的短信有多少,你就写多少,写完了就去看下条短信,完全不用卡在这等这个人,如果没有短信进来,你还可以休息一会。 这么说 是不是又更清楚了?? 在网络通讯中,服务端需要接收到一个完整的报文,才能交给应用层去处理,也就是说,你必须要 等电话那头的人把一整段文件念给你听,并且你已经全部写在纸上了,才能交给应用层。 你在你的题目中描述的场景,他不属于 NIO ,也不属于 BIO ,他是个纯应用层的场景, 他是发生在 接电话,或者读短信的那个人,把电话那头的人要发的文件全部写完之后,才发生的事。 这个层面 其实用什么 IO 都是一样的, 但是你现在好像只有这一层的概念,对 IO 那一层完全没概念,所以出现错误的结论 也不是稀奇, 再继续加油吧,以后学深一点再来质疑。 |
149
Joker123456789 2022-04-20 16:49:13 +08:00
@nothingistrue NIO 是同步非阻塞, 而且 IO 最大的应用场景 是 做网络通讯,不是读写文件。
|
150
araaaa 2022-04-20 17:35:33 +08:00
当你有几万条连接需要同时调度的时候就知道非阻塞的用处了
|
151
documentzhangx66 2022-04-20 17:41:02 +08:00
建议先玩一下 Factorio 这款游戏,以可视化的方式展示这类问题。
|
152
aguesuka 2022-04-21 11:56:01 +08:00
楼主还是表达有问题, 你直接贴知乎答案
``` 假如有 10000 个连接,4 核 CPU ,那么 bio 就需要一万个线程,而 nio 大概就需要 5 个线程(一个接收请求,四个处理请求)。如果这 10000 个连接同时请求,那么 bio 就有 10000 个线程抢四个 CPU ,几乎每个 CPU 平均执行 2500 次上下文切换,而 nio 四个处理线程,几乎每个线程都对应一个 CPU ,也就是几乎没有上下文切换。效率就体现出来了。 链接: https://www.zhihu.com/question/59356897/answer/164387902 ``` 当然是错误的 ``` 说下我的理解哈: 前提:BIO 面向流,NIO 面向缓冲区 10000 个连接,4 核 CPU ,如果是 BIO ,那么 Client ->OS 这个过程是阻塞的,当其中 4 个线程获取到 CPU 等待 OS 读取资源,那么剩余 9996 个线程就阻塞等待 T1,假设 1 个线程等待 OS 读取资源需要阻塞 1s(其他耗时先不考虑哈),那么剩下的线程就需 9996/4s ,这就可怕了。 而 NIO 呢,当其中 4 个线程获取到 CPU 资源去缓冲区读取资源,发现 OS 还没把资源放到缓冲区中,就释放 CPU 资源去干其他事,让其他线程来试试。这就节省了阻塞时间。当然了,如果线程发现缓冲区有准备好的数据的时候,效率和 BIO 还是一样的,其他线程还是要等这 4 个线程读取完数据的(同步)。 个人理解,有偏差了还请大佬帮忙指正下。 ``` 楼下的小白就给误导了 |
153
diagnostics 118 天前
OP 现在还有新的理解吗?
NIO 的提升应该是用单个 IO 线程处理更多线程,实际的业务处理里面 NIO 和 BIO 是没有区别的。例如 Kafka 一个 Selector 负责 Accept ,一个线程池 Selector 负责读写就是因为单个 Selector 处理 Accept 和读写有瓶颈,希望更多的线程留给业务线程 > nio 性能压榨 bio , 其实是错的。我自己用 4 核 8g 的服务器测试过。webflux 并不能缩短应用处理时间,只有让时间更加平稳,而普通 bio 的处理时间波动相当大。 这个是因为 BIO 会导致 Queuing networks ,你可以看看相关论文,一般会有 LayerQueueingNetworks 来建模分析和优化 |
154
jeesk OP @diagnostics
> nio 性能压榨 bio , 其实是错的。我自己用 4 核 8g 的服务器测试过。webflux 并不能缩短应用处理时间,只有让时间更加平稳,而普通 bio 的处理时间波动相当大。 这里的观点是什么? 你看懂了吗? |