关于使用异步框架发送 http 请求的疑问

2021-04-28 17:32:17 +08:00
 yumenawei
业务流程:
客户端请求服务端 -> 服务端请求多个第三方 http api 获取数据 -> 服务端聚合处理第三方 api 的数据,返回给客户端

在第二个步骤中,我们使用 vert.x web client 这个异步框架同时发起多个 http 请求,当请求完成后将结果放到一个 future 中。调用线程调用 future.get 阻塞住,等待异步请求结果返回。

我的问题是:
听说这个地方使用异步框架能带来处理能力上的提升,支持更多的请求和链接,这是为啥呢?不太理解。
相比较而言,如果换成使用线程池来发起多个 http 也用 future 来接受结果,到底提升在哪呢?
异步请求 http 的时候也是用的线程呀,这个线程与线程池里的线程有啥区别吗?凭啥更好呢?
3046 次点击
所在节点    Java
20 条回复
ch2
2021-04-28 18:13:16 +08:00
异步的协程不是真正的线程,你可以理解为开成千上万个对机器性能的压力都是毛毛雨
多线程是可以达到一样的效果,但是额外的 CPU 跟内存开销大了很多
并且多线程还容易出 bug 不好调试,代码逻辑更是无端变复杂了很多
改成异步之后你单个线程一秒钟就可以发一千个请求毫无压力,直接一个就能顶一千个线程
你还可以开 n 个线程,每个线程都用异步
buff 可以叠加何乐而不为?
yumenawei
2021-04-28 20:20:11 +08:00
@ch2 #1 老哥,你好。
Java 是没有协程的,只有线程。你说的协程应该是其他语言的。
所以异步里应该也是用的线程。
没有啥特别的优势。
ikas
2021-04-28 21:30:28 +08:00
http 异步是让你减少 cpu 的浪费,再等待结果的过程中,可以继续执行其他的调用
BBCCBB
2021-04-28 21:57:46 +08:00
future.get 还是会阻塞住, 完全异步应该是要针对 future 加个 listener 回调, 在回调里处理. 可以用 reactor 这种写法. 或者 CompletableFuture..
micean
2021-04-28 22:23:19 +08:00
vertx 的 httpclient 同样也是使用线程池,future.get 应该是 java.util.concurrent 包里的
异步只是为了提醒和帮助你更好的使用非阻塞,减少线程数。
大多数实际场景异步同步的性能差距并不太明显。
cloud107202
2021-04-28 23:21:12 +08:00
使用异步的服务伸缩性更好,比如 spring boot 同步的 web 需要预估请求量来配置线程池的最小最大值。
最大值配小了遇到高峰请求服务不过来,高峰过了又在 pool 里空等
kaneg
2021-04-28 23:49:10 +08:00
单纯的异步用线程池就可以,但要极大地提高性能,底层的 http 客户端应该要用 NIO 才行。
ipwx
2021-04-29 00:00:09 +08:00
1. 用 future.get 就错了。
2. https://www.v2ex.com/t/772976#reply28
wqhui
2021-04-29 09:27:08 +08:00
http 请求发送完成到接到响应这过程是主要耗时,在你用 get 阻塞之前还能多发几个 http 请求,同时等待响应。如果说线程池内的线程表现更好,有两个可能,一是响应是靠回调的,就是三楼说的 listener 写法,不需要阻塞(按你自己的阻塞写法,理论上是会有时间浪费在那);二是线程复用
gaius
2021-04-29 09:41:24 +08:00
用 webflux 的 webclient
yumenawei
2021-04-29 11:24:21 +08:00
@ikas #3 感觉只是一部分原因,结果的获取仍然需要有人去执行,这个地方仍然会用 cpu

@BBCCBB #4 这个地方我们可能没有处理的太好,后面研究下

@micean #5 我往下看了下,vertx 好像用了 netty 做了客户端的 nio,不大确定,还在看,老哥了解吗?

@cloud107202 #6 那这个也不是伸缩性好吧?而是性能和处理效果更高?

@kaneg #7 看了下,好像底层是用的 netty 做的客户端 nio,还在看,感谢提供思路

@ipwx #8 感谢分享。因为服务需要调用的结果,所以用的 future.get ,请问正确的用法是?

@wqhui #9 感谢,应该是底层用了客户端的 nio 来处理请求,提升性能

@gaius #10 老哥,应该是与框架没太大关系,我想了解的是上了这个框架,为啥性能和效果更高。
BBCCBB
2021-04-29 14:08:32 +08:00
为啥性能更好这个..


可以参考传统的网络编程, 一个线程处理一个连接, 大部分时间都没有读写, 线程经常在睡眠和睡眠状态来回切换, 大量时间和 cpu 花在 context swtich 上,

而 nio 这种. 一个或几个线程就能处理大量连接, context switch 很少,能把 cpu 利用起来
unco020511
2021-04-29 16:06:05 +08:00
jvm 没有协程,所以基于 jvm 语言的协程 api 都是用的线程
7075
2021-04-29 17:41:27 +08:00
可以参考一下 icop 模型和 epoll 模型,
异步的本质就是不浪费机器资源在阻塞上。
micean
2021-04-29 21:42:08 +08:00
@yumenawei

vertx 本来就是 netty pro,无论是线程还是 Buffer 都是对 netty 的封装,Vertical 和 Context 那一套保证线程安全的东西才是属于 vertx 的
passerbytiny
2021-04-30 09:54:46 +08:00
异步的重点不是线程池 /协程( Java 只有线程池没有协程),而是多任务切换。

你这个总是使用 future.get ,等同于同步调用(虽然比常规同步会好那么一丁点),并不能提高并发能力。要用到 listener 才能发挥异步的真正实力。
ikas
2021-04-30 16:56:37 +08:00
@yumenawei 你要把异步 io 与线程池 /线程这些分开去想 .假设你用异步 io 的 httpclient,那么你可以在一个线程中调用这个 client10 次,然后在这个线程中使用 get 等待.如果你用同步 io 的 httpclient,那么你要实现这样,就是要开 10 个线程,那如果更多呢
yumenawei
2021-05-06 16:12:55 +08:00
@micean #15 感谢指导。
@passerbytiny #16 嗯嗯,还有就是请求用了 nio,不用一个线程一直等待结果。
@BBCCBB #12 当初没想到客户端也可以用 nio
@ikas #17 是,我被异步这个东西限制住了思考
CantSee
2021-05-12 18:20:51 +08:00
通过线程池进行多线程调用时,业务代码是并行的,但是在 future.get 是阻塞的吧()
yumenawei
2021-05-12 18:51:27 +08:00
@CantSee #19 对,一直阻塞直到超时或者有结果返回。

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

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

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

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

© 2021 V2EX