Java 中并发请求多个接口怎样才能效率最高呢? - V2EX
noble4cc
V2EX    Java

Java 中并发请求多个接口怎样才能效率最高呢?

  •  1
     
  •   noble4cc Mar 26, 2020 9958 views
    This topic created in 2237 days ago, the information mentioned may be changed or developed.

    在 Java 中并发的请求多个接口,把请求来的数据做个聚合然后返回,如果是调用了 api1 然后再调用 api2 再调用 api3,这种方式可能大多数时间都在网络 IO 上了,而且随着接口的变多性能不断下降 如果是在 go 中直接用协成就可以了,请求五个 api 和请求一个 api 耗时可能差不多(前提 api 平均耗时都一样)

    java 中该如何编写代码呢?使用线程池的话肯定提高效率有限,因为线程不是协成,数量不会太多,并发量大了都在线程池里排队了

    使用 NIO httpclient 可能效果好些,但是都必须写 callback,怎么判断所有的 api 都把结果成功返回了,然后我们要聚合接口,callback 写起来有些难受

    java 中这种场景应该很多见,一般会怎么处理呢?

    43 replies    2020-05-23 16:39:23 +08:00
    guyeu
        1
    guyeu  
       Mar 26, 2020   1
    线程池+CompletableFuture+聚合操作
    pursuer
        2
    pursuer  
       Mar 26, 2020
    使用 Kotlin,C#的 async await,使用 Java Promise 库自己封装或者等待 Project Loom
    guyeu
        3
    guyeu  
       Mar 26, 2020
    CompletableFuture 自带一个线程池,自己写的话可能比协程别扭一点,但是效率差不多
    xuanbg
        4
    xuanbg  
       Mar 26, 2020
    其实还是前端直接调各个接口拿数据效率高,前端 JS 天然就是异步模式的。
    coer
        5
    coer  
       Mar 26, 2020
    callback+CompletableFuture ?
    jamlee
        6
    jamlee  
       Mar 26, 2020
    RxJava 比较适合这种事情吧
    areless
        7
    areless  
       Mar 26, 2020 via Android
    nginx lua 抗下大半
    Artiano
        8
    Artiano  
       Mar 26, 2020
    RxJava zip,用 Kotlin async/await 特别爽
    123444a
        9
    123444a  
       Mar 26, 2020 via Android
    肯定是 callback 丫大哥,多线程不需要加锁
    123444a
        10
    123444a  
       Mar 26, 2020 via Android
    callback 都是在 io 线程的,然后唤醒工作线程在工作线程判断收完 response 没,然后记得设置超时也要 callback
    CoderGeek
        11
    CoderGeek  
       Mar 26, 2020
    Future rx
    araaaa
        12
    araaaa  
       Mar 26, 2020 via iPhone
    rxjava spring reactor
    th00000
        13
    th00000  
       Mar 26, 2020
    异步可解
    xhinliang
        14
    xhinliang  
       Mar 26, 2020
    CountDownLatch
    gz911122
        15
    gz911122  
       Mar 26, 2020
    rxjava 了解一下

    或者 kotlin 协程
    Kipp
        16
    Kipp  
       Mar 26, 2020 via iPhone
    最近也同样遇到这个问题 mark
    yungo8
        17
    yungo8  
       Mar 26, 2020 via Android
    futuretask 短板是时间最长的那个接口
    HsuanLuk
        18
    HsuanLuk  
       Mar 26, 2020
    以往项目中是用 Future,mark 一下 JDK8 新特性
    micean
        19
    micean  
       Mar 26, 2020
    java 的 vertx 可以这么用

    CompositeFuture.all(请求 1,请求 2...)
    .compose(结果集 -> 处理结果集,返回最终结果)
    .setHandler(成功时的处理最终结果,至少一项请求失败时处理异常)

    和 java 自带的 CompletableFuture 相比,只用了 1 个线程,无阻塞。
    mosliu
        20
    mosliu  
       Mar 26, 2020
    jdk8
    CompletableFuture allof
    Macolor21
        21
    Macolor21  
       Mar 27, 2020 via iPhone
    以前做个类似场景,用创建个线程池,然后用 CountdownLatch 。看楼上似乎 8 的特性也支持。建议楼主写多个版本,做下 benchmark
    noble4cc
        22
    noble4cc  
    OP
       Mar 27, 2020
    @guyeu 线程池在并发量大的情况下不如协成吧
    感觉线程池的原理是使用多线程进行 http 请求,比如 5 个 api,开 5 线程,然后聚合,但是每个线程在执行的时候 io 是阻塞的,大部分的线程时间都浪费在阻塞上了,如果我们这种聚合 api 数据的请求特别多,比如 1000qps,复用五个线程或者多开点 20 个,相当于 1000 个要请求 5000 次后端 api,在线程池里排队处理的话太慢吧
    tairan2006
        23
    tairan2006  
       Mar 27, 2020 via Android
    这不是基本功么,开线程等待完成,CountDownLatch 啊
    Seawalker
        24
    Seawalker  
       Mar 27, 2020 via Android
    标记一下看看有没有好方案
    shaoyijiong
        25
    shaoyijiong  
       Mar 27, 2020
    一楼标准答案
    yc8332
        26
    yc8332  
       Mar 27, 2020
    只是聚合请求,干嘛不搞个现成的 api 网关就好了。。
    piglovesx
        27
    piglovesx  
       Mar 27, 2020
    小白一枚,很好奇协程是从哪个英文单词翻译过来的,是 channel 吗?
    guolaopi
        28
    guolaopi  
       Mar 27, 2020
    C#:Task.WaitAll();
    (滑稽
    LosLord
        29
    LosLord  
       Mar 27, 2020
    CompletableFuture.allOf(List<CompletableFuture>)
    noble4cc
        30
    noble4cc  
    OP
       Mar 27, 2020
    @tairan2006 老哥我说过多线程方案性能肯定不行
    200 qps 访问 5 个 api 不能开 1000 个线程吧,线程复用一个机器 8core 开 16 个工作线程的话,每次并发的请求后端 api 是 16,每个 api 平均耗时 10ms 的话,第 200 个请求得等到什么时候呢?量少了确实没什么问题,java 类似的工具包确实也多如牛毛
    noble4cc
        31
    noble4cc  
    OP
       Mar 27, 2020
    @micean 这个本质上确实是 io 多路复用的原理吧,开起来挺方便的,vert.x 不太熟,netty 到是经常用,我一开始想的是用 netty 封装个 httpclient,但是感觉搞起来太麻烦了,是不是 vert.x 就是用 netty 实现了 http 协议了
    lscexpress
        32
    lscexpress  
       Mar 27, 2020
    @piglovesx Coroutine 翻译为协程,通常来说 java 不用协程。channel 在书中的翻译多为信道或者通道
    piglovesx
        33
    piglovesx  
       Mar 27, 2020
    @lscexpress 谢谢 :)
    guyeu
        34
    guyeu  
       Mar 27, 2020
    @noble4cc #22 是的,线程池在并发量大的情况下不如协程。所以这种情况下会做一些设计,比如把发消息和收消息分开,一个线程池专门发,一个线程池专门处理收消息,也就是 NIO 的思路。。
    hpeng
        35
    hpeng  
       Mar 27, 2020 via iPhone
    看一楼的
    aguesuka
        36
    aguesuka  
       Mar 27, 2020 via Android
    @noble4cc vertx 底层就是 netty
    buliugu
        37
    buliugu  
       Mar 27, 2020
    java 大量 API 请求可以用 Quasar,现成的纤程库
    elevation
        38
    elevation  
       Mar 30, 2020
    不知道你现在怎么样,我觉得用 diruptor,环形数组线程分发,可以降低消耗,自己写底层实现,工厂,资源调用。比较方便;
    xiaoidea
        39
    xiaoidea  
       Mar 31, 2020
    目前用的是线程池+guava ListenableFuture 、Futures 工具类,确实很多线程堵在 IO 上了,线程池要开多大需要压测
    看到有其他项目用 Spring webflux 的,对这个不熟
    monkeyWie
        40
    monkeyWie  
       Apr 3, 2020
    NIO httpclient + CountDownLatch 不就行了吗
    主线程还是得阻塞的啊,阻塞到 api 全部 callback 完
    RRRSSS
        41
    RRRSSS  
       Apr 8, 2020
    Completable<Void> f = CompletableFuture.allOf(task1, task2, task3); // 这里注意要使用线程池
    f.get(); // 这里消耗的时间是 task1 、task2 、task3 的最大值

    Stream.of(future1, future2, future3).forEach(dd -> dd.thenAccept(e -> {
    // 处理数据
    }));
    guisheng
        42
    guisheng  
       May 23, 2020
    楼主最后使用了什么方式呢?我目前采用的是 spring webclient 的 Mono.zip 来组合请求发送。
    noble4cc
        43
    noble4cc  
    OP
       May 23, 2020
    @guisheng NIO 的 httpclient+队列吧,编写起来也不太麻烦,毕竟不是 golang
    About     Help     Advertise     Blog     API     FAQ     Solana     1175 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 108ms UTC 23:11 PVG 07:11 LAX 16:11 JFK 19:11
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86