noble4cc
V2EX  ›  Java

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

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

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

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

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

    Stream.of(future1, future2, future3).forEach(dd -> dd.thenAccept(e -> {
    // 处理数据
    }));
    cameco
    cameco
    May 23, 2020
    42
    楼主最后使用了什么方式呢?我目前采用的是 spring webclient 的 Mono.zip 来组合请求发送。
    noble4cc
    noble4cc
    OP
    May 23, 2020
    43
    @guisheng NIO 的 httpclient+队列吧,编写起来也不太麻烦,毕竟不是 golang
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   2313 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 89ms · UTC 00:29 · PVG 08:29 · LAX 16:29 · JFK 19:29
    ♥ Do have faith in what you're doing.