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

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

  •  
  •   liaojl · 2019-05-29 20:10:08 +08:00 via Android · 8950 次点击
    这是一个创建于 2006 天前的主题,其中的信息可能已经有所发展或是发生改变。
    服务端需要调用第三方 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
    oneisall8955
        2
    oneisall8955  
       2019-05-29 20:27:19 +08:00 via Android
    用变量来记录当前时间分钟对应的调用次数可以否?到下一分钟,重置为 0,这分钟内每次调用增加 1,到 500 提示失败
    oneisall8955
        3
    oneisall8955  
       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
    网关也能干出去调用的,代理一下就行。
    mejee
        28
    mejee  
       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   ·   实用小工具   ·   2505 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 15:45 · PVG 23:45 · LAX 07:45 · JFK 10:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.