请教, Java 怎么控制 API 调用的频率 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
liaojl
V2EX    Java

请教, Java 怎么控制 API 调用的频率

  •  
  •   liaojl 2019-05-29 20:10:08 +08:00 via Android 9571 次点击
    这是一个创建于 2412 天前的主题,其中的信息可能已经有所发展或是发生改变。
    服务端需要调用第三方 API,但是第三方 API 有调用频率限制,多次超出调用频率会被拉黑。目前用线程池来调 API,怎么才能控制服务器端每分钟调用次数不超过指定数量,比如每分钟不超过 500 次。
    36 条回复    2019-05-30 19:52:38 +08:00
    micean
        1
    micean  
       2019-05-29 20:23:56 +08:00   2
    guava 有 ratelimiter
    itechify
        2
    itechify  
    PRO
       2019-05-29 20:27:19 +08:00 via Android
    用变量来记录当前时间分钟对应的调用次数可以否?到下一分钟,重置为 0,这分钟内每次调用增加 1,到 500 提示失败
    itechify
        3
    itechify  
    PRO
       2019-05-29 20:28:34 +08:00 via Android
    我渣渣哈哈~没做过这个东西,胡乱猜想的
    liaojl
        4
    liaojl  
    OP
       2019-05-29 20:29:14 +08:00 via Android
    @micean 好东西,正是我想要的
    liaojl
        5
    liaojl  
    OP
       2019-05-29 20:32:51 +08:00 via Android
    @oneisall8955 我也这么想过,但是后来一想,如果负责置 0 的那个线程阻塞或者发生什么意外,那就 gg 了
    runtu2019
        6
    runtu2019  
       2019-05-29 20:49:26 +08:00
    @liaojl 同时放个时间戳,变量只做自增作用,同时校验时间戳和数量,单个线程崩了应该没事
    liaojl
        7
    liaojl  
    OP
       2019-05-29 21:10:25 +08:00 via Android
    @runtu2019 好办法
    securityCoding
        9
    securityCoding  
       2019-05-29 21:20:55 +08:00
    1. guava 单机限流
    2.阿里开源的 Sentine
    l8g
        10
    l8g  
       2019-05-29 21:33:12 +08:00
    单机可以看下 Guava 的 RateLimiter
    分布式场景下用 Redis 的 zset 做个简单的限流也可以
    palmers
        11
    palmers  
       2019-05-29 21:33:13 +08:00
    liaojl
        12
    liaojl  
    OP
       2019-05-29 21:46:38 +08:00
    @securityCoding
    @l8g
    @palmers
    谢谢各位!
    dawncold
        13
    dawncold  
       2019-05-29 23:11:09 +08:00
    一个好的 API 设计会告诉你当前时间窗口还剩多少次调用,遵照执行就好
    gz911122
        14
    gz911122  
       2019-05-29 23:13:25 +08:00
    rxjava 被压
    CoderGeek
        15
    CoderGeek  
       2019-05-30 01:31:51 +08:00
    简单的限流就可以实现 楼上都说了
    xuanbg
        16
    xuanbg  
       2019-05-30 03:28:02 +08:00
    redis 里面搞一个过期时间为 1 分钟的 key 作为计数器,读到计数器到达 500 了,就报调用失败或者扔到队列里面去等下一分钟再调用。队列可以满足削峰的需求,但不能满足你每分钟平均次数超过 500 的需求。如果平均每分钟超过 500 的话,只能报错了。
    jorneyr
        17
    jorneyr  
       2019-05-30 07:47:45 +08:00
    lihongjie0209
        18
    lihongjie0209  
       2019-05-30 08:58:08 +08:00
    老老实实写日志到 redis 或者是数据库中, 调用之前先查询一下日志.

    基于 guava 这种内存型的, 服务器重启就 GG 了.
    aino
        19
    aino  
       2019-05-30 09:15:46 +08:00
    俺用的 RateLimiter
    luozic
        20
    luozic  
       2019-05-30 09:18:07 +08:00 via iPhone
    kong/其他平台的网关
    liaojl
        21
    liaojl  
    OP
       2019-05-30 09:42:44 +08:00 via Android
    @jorneyr 是限制我方调用第三方 API 的频率,不是限制客户端调我方 API 的频率,不过还是感谢哈,Nginx 还可以这么用
    liaojl
        22
    liaojl  
    OP
       2019-05-30 09:44:05 +08:00 via Android
    @lihongjie0209 哈哈,只是限制每分钟的调用频率,不限制历史总的调用次数的
    lzxz1234
        23
    lzxz1234  
       2019-05-30 10:31:58 +08:00
    选定一个时间单位,记录这个时间段内所有请求发出去的时间,每次请求时查看最早的那个请求时间,大于时间单位通过并更新请求时间,否则等待或者返回 false,一个循环数组就可以搞定了,根据目标频率选一个合适的大小就可以
    clarkyi
        24
    clarkyi  
       2019-05-30 11:41:23 +08:00
    guava 这种不支持分布式吧?多台设备就 gg 了,redis+guava 考虑一下
    lihongjie0209
        25
    lihongjie0209  
       2019-05-30 11:55:00 +08:00
    @liaojl
    00:00:03 秒调用了 300 次
    00:00:05 服务器重启
    00:00:40 重启成功
    00:00:59 又调用了 300 次

    总数超了
    cyhulk
        26
    cyhulk  
       2019-05-30 12:46:48 +08:00
    @lihongjie0209 要看场景的,如果是考虑性能问题限制次数,重启了,重新 300 也无所谓,反正服务器重启过了,300 也压的住,直接内存,相对于 redis 调用的性能损耗,要划算的多
    luozic
        27
    luozic  
       2019-05-30 12:53:07 +08:00 via iPhone
    网关也能干出去调用的,代理一下就行。
    temp178
        28
    temp178  
       2019-05-30 13:02:51 +08:00 via Android
    单机的话简单的限流算法就可以 令牌桶算法之类的
    vtoee
        29
    vtoee  
       2019-05-30 13:10:55 +08:00
    有点类似于调百度地图 API 坐标转换
    lihongjie0209
        30
    lihongjie0209  
       2019-05-30 13:15:23 +08:00
    @cyhulk 这个限制是第三方 API 的, 不是楼主的
    EastLord
        31
    EastLord  
       2019-05-30 13:17:39 +08:00
    目测 阿里的 Sentine 满足楼主需求
    zzzmode
        32
    zzzmode  
       2019-05-30 13:23:04 +08:00
    kong 网关
    opengps
        33
    opengps  
       2019-05-30 13:27:11 +08:00
    如果是单机应用,服务器上装个安全狗之类的防护工具,可以对所有请求整体频率起效果。
    如果只是某个接口调用,套了 cdn 则被忽视,不套上 cdn 的业务需要做个公共缓存统计,记录一个列表,每次请求顺便释放掉超时的缓存对象
    opengps
        34
    opengps  
       2019-05-30 13:29:08 +08:00
    如果是单机单接口,那么可以利用系统自带缓存,或者本机 memcached 缓存做个计数(取出结果非等于 1 则++,等于 null 则赋值 1 ),超时时间就设置 1 分钟
    gaius
        35
    gaius  
       2019-05-30 14:24:41 +08:00
    这问题我遇到过,1 时 0 分 1s 发了 500 个,2 时 0 分 2s 又发了 500,从 1s 到 2s 的这个时间窗口却是 1 小时发了 1000。
    如果消费还有优先级的话,还再麻烦点。
    gscoder
        36
    gscoder  
       2019-05-30 19:52:38 +08:00
    和限流是一样的方法:漏桶算法、令牌桶算法、滑动窗口算法。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5432 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 07:22 PVG 15:22 LAX 23:22 JFK 02:22
    Do hav 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