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

一个聚合接口,如何才能优化时延

  •  
  •   luxinfl · 2021-10-26 22:18:01 +08:00 · 4306 次点击
    这是一个创建于 874 天前的主题,其中的信息可能已经有所发展或是发生改变。
    接口逻辑中会调用 6,7 次外部接口查询数据,然后再把结果汇总。现在用的是 parallelStream 来分别调用这几个外部接口,但是效果貌似不太好。还有什么其他方法可以并行调用的?
    第 1 条附言  ·  2021-10-28 23:53:37 +08:00
    之前用的 list.parallel.xxx 没有用自定义 ForkJoinPool ,改成自定义线程池后,貌似变好了些
    30 条回复    2021-10-29 10:31:04 +08:00
    pushyzheng
        1
    pushyzheng  
       2021-10-26 22:20:18 +08:00
    Reactor 挺适合这种场景的
    Jooooooooo
        2
    Jooooooooo  
       2021-10-26 22:21:39 +08:00
    并行调用耗时就是最慢的那个接口, 这个角度没啥可优化的点了.
    luxinfl
        3
    luxinfl  
    OP
       2021-10-26 22:37:43 +08:00
    @pushyzheng 有点太复杂了,不太适合吧。我应用场景就是并发调用接口

    @Jooooooooo 实现不太一样吧,有时候还会碰到从连接池获取连接超时。。
    luxinfl
        4
    luxinfl  
    OP
       2021-10-26 22:42:31 +08:00
    主要是测试压测有个时延要求,p95 要到 50ms ,但是这个服务需要调用多个外部接口。没什么优化经验。。用了 parallelStream ,增大的连接池的 defaultMaxPerRoute 。效果不是太好,就在想是不是 parallelStream 有什么缺陷
    urzz
        5
    urzz  
       2021-10-26 22:45:46 +08:00
    建议不要用 parallelStream ,ForkJoinPool 是全局共用的。。
    luxinfl
        6
    luxinfl  
    OP
       2021-10-26 23:09:44 +08:00
    @urzz 如果定义那个 ForkJoinPool 有啥问题么
    urzz
        7
    urzz  
       2021-10-27 01:13:56 +08:00
    你如果可以保证没有别人用 parallelStream ,那可以。要不然这种在 parallel 中进行 io 调用的,是可能会导致阻塞的

    这种情况下,可以尝试自己定义线程池,然后用 CompletableFuture.supplyAsync ,配合 CompletableFuture.allOf(...).join() 等待线程结束获取结果。建议改完后压一波试试
    w7938940
        8
    w7938940  
       2021-10-27 01:31:51 +08:00
    Fibers + Channel
    VHacker1989
        9
    VHacker1989  
       2021-10-27 08:20:41 +08:00 via Android
    分布式
    aeiou520
        10
    aeiou520  
       2021-10-27 08:58:23 +08:00
    CompletableFuture?
    siweipancc
        11
    siweipancc  
       2021-10-27 09:01:45 +08:00 via iPhone
    多线程加阻塞同步器,怕并发太大可以塞信号量
    nl101531
        12
    nl101531  
       2021-10-27 09:11:24 +08:00 via iPhone
    @luxinfl 这个用的是 forkjoin 公共线程池,你系统用的多,就可能排队阻塞
    Aliberter
        13
    Aliberter  
       2021-10-27 09:53:22 +08:00
    自定义线程池+CountDownLatch ,说到底总耗时还是取决于最慢的那个接口的响应时间。
    wolfie
        14
    wolfie  
       2021-10-27 09:54:26 +08:00
    可以自定义 ForkJoinPool

    forkJoinPool.execute(e -> {
    someList.parallelStream()
    })
    hingbong
        15
    hingbong  
       2021-10-27 10:20:16 +08:00
    用 parallelStream 都会自定义 ForkJoinPool 吧,问题不大
    Vegetable
        16
    Vegetable  
       2021-10-27 10:22:59 +08:00
    先做好日志确认一下,确认是否「总用时~=耗时最长的外部服务」,如果是的话,就没什么优化的空间了,如果不是再排查吧,按理说这么做没问题。
    Chinsung
        17
    Chinsung  
       2021-10-27 11:09:07 +08:00
    查数据就是并发+缓存,没啥别的办法。
    8355
        18
    8355  
       2021-10-27 14:26:48 +08:00
    除非提前调用直接查 不然并行调用还是会以最慢的接口时间
    如果因为网络或者机器位置的关系找运维给你加代理网关会好很多
    xiang0818
        19
    xiang0818  
       2021-10-27 15:09:12 +08:00
    这个没办法解决的,外部接口的调用时间在于别人服务器对你的响应时间。
    night98
        20
    night98  
       2021-10-27 18:30:19 +08:00
    提前聚合
    WispZhan
        21
    WispZhan  
       2021-10-27 19:10:53 +08:00
    最直接的解 CompletableFuture ,这玩意写起来贼恶心。
    比较优雅的解 Kotlin 协程
    makdon
        22
    makdon  
       2021-10-27 19:25:10 +08:00
    无解,就算你的六七个接口都没有互相依赖,全并行一起请求外部,
    访问外部来回 rtt 也是要耗时的,外部接口再稍微慢一点你的 p95 就到不了 50ms 了
    除非把机房搬外部接口提供方机房旁边,不然跨一下地域轻轻松松就过 50ms 了,不过你这个 case 还有 6 ,7 次外部接口,也不知道是不是同一个提供商,不是的话机房都搬不动了

    随便搜了一下时延的经验值
    腾讯云:

    北京到上海:38ms
    上海到广州:40ms
    北京到广州:53ms
    az402
        23
    az402  
       2021-10-28 09:06:36 +08:00
    <groupId>com.jd.platform</groupId>
    <artifactId>asyncTool</artifactId>
    <version>1.4.1-SNAPSHOT</version>

    可以看一下这个,前段无意中发现的。
    还没仔细看,貌似不错。
    jorneyr
        24
    jorneyr  
       2021-10-28 11:27:02 +08:00
    CompletionService 或者 CompletableFuture 挺好用的,还可以自己传入 ThreadPoolExecutor
    snappyone
        25
    snappyone  
       2021-10-28 13:35:19 +08:00
    p95 要到 50ms , 你先确定下你依赖的那几个接口能到这个速度吗
    luxinfl
        26
    luxinfl  
    OP
       2021-10-28 21:32:45 +08:00
    @hingbong 项目里面真的好多 paralleStream 都没有自定义线程池。每个 parallel 都自定义一个是不是不太好
    @Vegetable 奇怪的是,每个并发流时间都不高,但是最终却很高,怀疑是用了共享线程池的缘故
    @WispZhan @aeiou520 @urzz 我用的 CompletableFuture 然后自定义线程池,但是用了 get()方法,感觉就和 Future 差不多的样子
    urzz
        27
    urzz  
       2021-10-28 23:35:58 +08:00
    @luxinfl #26 原理是差不多的,都是异步同时请求接口,而不是串行。只不过这种方式可以将任务丢进线程池,而不是每次 new 线程跑。
    不过这种方式是否能满足你需求也是母鸡的。。毕竟这种聚合接口的耗时要看你依赖接口的
    billly
        28
    billly  
       2021-10-29 09:25:42 +08:00
    之前看过 linkedin/parseq ,看起来还行,没用过
    luxinfl
        29
    luxinfl  
    OP
       2021-10-29 09:43:23 +08:00 via Android
    @billly 现在改用 CompletableFuture 试试会不会好,实际用起来,感觉并发流不太好。
    KuroNekoFan
        30
    KuroNekoFan  
       2021-10-29 10:31:04 +08:00
    试试 nodejs(
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3531 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 11:04 · PVG 19:04 · LAX 04:04 · JFK 07:04
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.