V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yumenawei
V2EX  ›  Java

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

  •  
  •   yumenawei · 2021-04-28 17:32:17 +08:00 · 3107 次点击
    这是一个创建于 1340 天前的主题,其中的信息可能已经有所发展或是发生改变。
    业务流程:
    客户端请求服务端 -> 服务端请求多个第三方 http api 获取数据 -> 服务端聚合处理第三方 api 的数据,返回给客户端

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

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


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

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

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

    你这个总是使用 future.get ,等同于同步调用(虽然比常规同步会好那么一丁点),并不能提高并发能力。要用到 listener 才能发挥异步的真正实力。
    ikas
        17
    ikas  
       2021-04-30 16:56:37 +08:00
    @yumenawei 你要把异步 io 与线程池 /线程这些分开去想 .假设你用异步 io 的 httpclient,那么你可以在一个线程中调用这个 client10 次,然后在这个线程中使用 get 等待.如果你用同步 io 的 httpclient,那么你要实现这样,就是要开 10 个线程,那如果更多呢
    yumenawei
        18
    yumenawei  
    OP
       2021-05-06 16:12:55 +08:00
    @micean #15 感谢指导。
    @passerbytiny #16 嗯嗯,还有就是请求用了 nio,不用一个线程一直等待结果。
    @BBCCBB #12 当初没想到客户端也可以用 nio
    @ikas #17 是,我被异步这个东西限制住了思考
    CantSee
        19
    CantSee  
       2021-05-12 18:20:51 +08:00
    通过线程池进行多线程调用时,业务代码是并行的,但是在 future.get 是阻塞的吧()
    yumenawei
        20
    yumenawei  
    OP
       2021-05-12 18:51:27 +08:00
    @CantSee #19 对,一直阻塞直到超时或者有结果返回。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2658 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 15:15 · PVG 23:15 · LAX 07:15 · JFK 10:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.