V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
EdwardLee
V2EX  ›  问与答

Java 如何设计频繁 HTTP 请求(至少 13w 次)的统计程序?

  •  
  •   EdwardLee · 2019-04-19 10:51:47 +08:00 · 4674 次点击
    这是一个创建于 2071 天前的主题,其中的信息可能已经有所发展或是发生改变。

    1.场景描述   
      现有基础数据 13w 条,需循环通过第三方 HTTP 接口获取历史数据进行分析统计,意味着要发生 13w 次 HTTP 请求。接口请求速度还比较慢,平均每次请求需要 1s,如果在单线程的情况下至少需要 36 个小时,目前我设计的是使用多线程每 3w 条作为一个任务进行请求,目前的消耗时间大概能减少到 16 小时左右,但效率还是太低了,有没有大佬有更好的解决方案的?求 Help!!

    2.部署环境   
    系统:CentOS7

    配置:单 CPU 双核

    49 条回复    2019-04-22 11:21:43 +08:00
    gaius
        1
    gaius  
       2019-04-19 11:04:25 +08:00
    1s 是网络问题还是对方处理的比较慢?
    EdwardLee
        2
    EdwardLee  
    OP
       2019-04-19 11:10:09 +08:00
    @gaius 对方处理慢
    EdwardLee
        3
    EdwardLee  
    OP
       2019-04-19 11:14:53 +08:00
    @gaius 请问您觉得在不减少 HTTP 请求次数的情况下,我这边还有什么更好的处理方式吗?
    2kCS5c0b0ITXE5k2
        4
    2kCS5c0b0ITXE5k2  
       2019-04-19 11:16:59 +08:00 via iPhone
    @EdwardLee 感觉没啥办法。。1s 太慢了
    peyppicp
        5
    peyppicp  
       2019-04-19 11:18:47 +08:00
    ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(100, 200,
    10, TimeUnit.MINUTES, new ArrayBlockingQueue<>(100),
    new NamedThreadFactory("trade-thread-", true), new ThreadPoolExecutor.CallerRunsPolicy());
    然后你主线程 for 循环提交 runnable 或者 callable 任务就完事了。200 条线程应该可以了
    EdwardLee
        6
    EdwardLee  
    OP
       2019-04-19 11:22:46 +08:00
    @emeab 是啊,太慢了,扎心
    orangeade
        7
    orangeade  
       2019-04-19 11:23:58 +08:00 via Android   ❤️ 1
    换 golang 然后无脑开 goroutine ?
    EdwardLee
        8
    EdwardLee  
    OP
       2019-04-19 11:28:13 +08:00
    @peyppicp 200 个线程会不会出问题啊?我试试。多线程不太会用,我是参照网上说的 IO 密集型任务应设置线程数为核心数的两倍,HTTP 请求应该属于网络 IO 吧。
    EdwardLee
        9
    EdwardLee  
    OP
       2019-04-19 11:29:36 +08:00
    @orangeade 这...
    xuwenping
        10
    xuwenping  
       2019-04-19 11:33:05 +08:00 via Android
    对方能不能也使用多线程?
    HuHui
        11
    HuHui  
       2019-04-19 11:33:25 +08:00 via Android
    让对方提供批量查询接口
    peyppicp
        12
    peyppicp  
       2019-04-19 11:42:04 +08:00   ❤️ 1
    @EdwardLee IO 密集型,线程数可以开到很高。CPU 密集型,线程数开到核心数。

    如果对方接口允许,我觉得开 500 线程都行
    honeycomb
        13
    honeycomb  
       2019-04-19 11:45:53 +08:00 via Android
    @EdwardLee spring webflux ?
    honeycomb
        14
    honeycomb  
       2019-04-19 11:47:23 +08:00 via Android
    @EdwardLee 楼主遇到的情况是每个线程大部分时间在等回复,这个意义上再多开一些(三五百个)也成。
    gz911122
        15
    gz911122  
       2019-04-19 11:52:58 +08:00
    用 netty 异步回调
    这种性能主要是卡在网络 io 的情况下用异步再合适不过了
    gz911122
        16
    gz911122  
       2019-04-19 11:53:36 +08:00
    @gz911122 或者就是楼上说的那样,往死里开线程
    feiyuanqiu
        17
    feiyuanqiu  
       2019-04-19 11:56:36 +08:00 via Android
    对方一个请求都能处理一秒,开几百个线程的不一下把人家服务器打死?
    问问看能不能提供批量查询接口吧,估计没戏
    az422
        18
    az422  
       2019-04-19 12:13:45 +08:00 via Android
    异步 httpclient,如 Apache 那个 httpasync 或者 vertx.client (多个实例),并设置 keepalive (双方)
    当然还是批量接口靠谱
    EdwardLee
        19
    EdwardLee  
    OP
       2019-04-19 12:25:19 +08:00
    @xuwenping
    @HuHui
    @feiyuanqiu 就是坑在这,第三方暂时不可控,增加批量接口行不通
    EdwardLee
        20
    EdwardLee  
    OP
       2019-04-19 12:27:46 +08:00
    @peyppicp 好的,谢谢,我先试试,有结果了我再来反馈
    rrfeng
        21
    rrfeng  
       2019-04-19 12:30:11 +08:00 via Android
    你算一下对方最多能提供每秒几个
    然后就写自己的并发量

    取决于对方而不是自己
    EdwardLee
        22
    EdwardLee  
    OP
       2019-04-19 12:30:45 +08:00
    @honeycomb 是的,瓶颈就卡在请求等待这里,我先试试多开线程看下结果
    EdwardLee
        23
    EdwardLee  
    OP
       2019-04-19 12:32:12 +08:00
    @gz911122
    @az422 异步的方式是不是也类似于多线程。需要取决于第三方接口的承受能力?
    lhx2008
        24
    lhx2008  
       2019-04-19 12:51:03 +08:00 via Android
    异步可以一下打 13 万条请求出去,对方回数据,操作系统会自动保存,然后程序可以慢慢拿返回结果。但是对方的服务器是妥妥的挂了。。

    所以先压测一下,看看同时打多少条出去
    MoHen9
        25
    MoHen9  
       2019-04-19 13:00:05 +08:00 via Android
    数据量有多大,返回怎么那么慢?还有为什么不能自定义获取条数?
    CoSpLi
        26
    CoSpLi  
       2019-04-19 13:12:37 +08:00 via iPhone
    对接的蛋疼事儿,并发量只能看对方接口了。。之前也碰到过,并发请求多了被对方拉黑了。。。
    az422
        27
    az422  
       2019-04-19 13:18:33 +08:00
    @EdwardLee 是取决于对方承受能力,但跟多线程本质不同,多线程本质会阻塞的,异步不会,在对方承受能力下能增大你这边的吞吐量。
    `我设计的是使用多线程每 3w 条作为一个任务进行请求,目前的消耗时间大概能减少到 16 小时左右` 像你这样设计,在对方 1s 内没有返回内你的线程是被阻塞不能干其他事的,完全浪费了性能。130000/(16*3600)约 2.25 ,相当于你 1s 才 3 个请求?要是对方能力真这样当我没说。。。 假设对方最大 qps=10000, 你可以开 10000 线程, (但是你单 CPU 双核 = =), 这种情况下还是用异步 IO 能压榨对方性能
    misaka19000
        28
    misaka19000  
       2019-04-19 13:22:45 +08:00 via Android
    异步操作
    33and66
        29
    33and66  
       2019-04-19 13:58:08 +08:00
    这种数据 建议生成 txt 文件 然后通过文件内容建议直接做
    方法很土 但是很实用
    我们现在和银行每天上千万的数据对账就是这么干
    liujan
        30
    liujan  
       2019-04-19 15:37:55 +08:00
    如果是对方数据加工处理比较慢的话,是不是可以先让他每天 定时离线处理好了数据,然后你再过去拉处理后的数据?
    whp1473
        31
    whp1473  
       2019-04-19 15:51:58 +08:00
    每天根据你的数据定时获取对方数据,将获取的数据保存起来,用户查询时查自己库,然后缓存起来。
    mritd
        32
    mritd  
       2019-04-19 15:57:14 +08:00 via iPhone
    可能这个回答不对路,但是如果这种需要大并发处理的我真的会上 golang 写,因为这样基本不需要带脑子....
    xdlucky
        33
    xdlucky  
       2019-04-19 15:57:40 +08:00
    联系对方直接买数据
    xzg
        34
    xzg  
       2019-04-19 17:44:51 +08:00
    看你响应结果是否需要,不需要就搞异步多线程。如果需要响应结果那就只能等待 1s,尝试开启多线程,实在不行部署多个服务并行发
    Yourshell
        35
    Yourshell  
       2019-04-19 20:39:07 +08:00
    寄 U 盘吧
    EdwardLee
        36
    EdwardLee  
    OP
       2019-04-22 09:49:17 +08:00
    @rrfeng
    @lhx2008 好的,感谢建议
    EdwardLee
        37
    EdwardLee  
    OP
       2019-04-22 09:52:26 +08:00
    @MoHen9 数据量不算大,从一条到两三千条 JSON 字符串不等,应该是数据需要处理比较慢吧
    EdwardLee
        38
    EdwardLee  
    OP
       2019-04-22 09:52:52 +08:00
    @CoSpLi 拉黑,这么过分的吗,Hahh
    EdwardLee
        39
    EdwardLee  
    OP
       2019-04-22 09:55:59 +08:00
    @az422 目前使用 30 个线程,处理时间已经减少到 3.5 小时,偶尔会有 HTTP 请求超时的情况出现。我再观察一下,感谢您的建议
    EdwardLee
        40
    EdwardLee  
    OP
       2019-04-22 09:56:52 +08:00
    @misaka19000 很多都是建议异步操作的,没怎么了解过,我试试看。感谢建议
    EdwardLee
        41
    EdwardLee  
    OP
       2019-04-22 10:00:46 +08:00
    @33and66 如果有现成的数据文件是可行,但是在第三方不改动的情况下,暂时也没有其他方式可以获取数据来源,还是得调接口请求
    EdwardLee
        42
    EdwardLee  
    OP
       2019-04-22 10:02:11 +08:00
    @liujan 第三方那边不可控,还是得我这边想办法优化一下。感谢建议
    EdwardLee
        43
    EdwardLee  
    OP
       2019-04-22 10:07:06 +08:00
    @whp1473 您建议的这种方式可能不太适合我这个场景,应该适合那种一次性响应数据量比较大的情况。目前接口并非因为响应数据量大而缓慢,所以即使定时获取也无法迈过多次 HTTP 请求这道坎,还是得想办法提高 IO 利用率
    EdwardLee
        44
    EdwardLee  
    OP
       2019-04-22 10:08:43 +08:00
    @mritd 好多大佬说 golang 的,不带脑子都行,那么强大的吗,hah
    mritd
        45
    mritd  
       2019-04-22 10:52:44 +08:00
    @EdwardLee #44 怎么说呢,就是相对于 java 开并发 心智负担小得多,基本上不用带脑子 😂
    EdwardLee
        46
    EdwardLee  
    OP
       2019-04-22 10:58:14 +08:00
    @Yourshell 每天寄 U 盘也挺累的,手动狗头🐶
    EdwardLee
        47
    EdwardLee  
    OP
       2019-04-22 11:00:11 +08:00
    @xzg 业务场景是不需要实时响应的,异步应该是可行的。暂时不太了解异步,尝试了下多线程,效果还是很明显的,已经能够满足需求了。
    EdwardLee
        48
    EdwardLee  
    OP
       2019-04-22 11:17:22 +08:00
    目前情况:
    1. 单线程:耗时约 36 小时,请求稳定(效率极低)
    2. 分批 3w 数据 /线程,共 5 个线程:耗时约 16 小时,请求较稳定 (效率略低)
    3. 固定 30 个线程:耗时约 3.5 小时,偶有 HTTP 请求超时(效率已经能够满足,需解决部分超时异常)
    4. 固定 100 个线程:测试过程因请求超时案例过多,暂不予考虑,所以未计耗时

    总结:
    我的业务场景属于网络 IO 密集型任务,时间基本都消耗在等待 HTTP 请求的响应,所以多线程请求还是效果很明显的,至于固定多少线程效率最高我无法确定,纯手工尝试😂,有科学测量方式的小伙伴麻烦指教一下。30 线程数已经能够满足业务需求,暂以项目交付为先,关于各位 V 友的建议比如异步请求之类的有空的时候再尝试。

    在此感谢各位 V 友的不吝赐教
    CoSpLi
        49
    CoSpLi  
       2019-04-22 11:21:43 +08:00 via iPhone
    @EdwardLee 被当做恶意爬虫了。那会儿写还真没考虑过这么多
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1229 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 37ms · UTC 18:07 · PVG 02:07 · LAX 10:07 · JFK 13:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.