API调用次数限制实现

2023-05-16

API调用次数限制实现

      在开发接口服务器的过程中,为了防止客户端对于接口的滥用,保护服务器的资源, 通常来说我们会对于服务器上的各种接口进行调用次数的限制。比如对于某个 用户,他在一个时间段(interval)内,比如 1 分钟,调用服务器接口的次数不能够 大于一个上限(limit),比如说 100 次。如果用户调用接口的次数超过上限的话,就 直接拒绝用户的请求,返回错误信息。

      这里采用Token Bucket(令牌桶)算法。 

           

      搜索资料的时候,发现 Guava 库当中也有一个 RateLimiter,其作用也是 用来进行限流,于是阅读了 RateLimiter 的源代码,查看一些 Google 的人是如何实现 Token Bucket 算法的。

       参考: RateLimiter 和 SmoothRateLimiter     

      在 resync 方法中的这句代码 storedPermits = min(maxPermits, storedPermits+ (nowMicros - nextFreeTicketMicros)/stableIntervalMicros); 就是 RateLimiter 中计算 Token 数量的方法。没有使用计时器,而是使用时间戳的方式计算。这个做法给足了 信息。 我们可以在 Bucket 中存放现在的 Token 数量,然后存储上一次补充 Token 的时间戳,当用户下一次请求获取一个 Token 的时候, 根据此时的时间戳,计算从上一个时间戳开始,到现在的这个时间点所补充的所有 Token 数量,加入到 Bucket 当中。

// com.google.common.util.concurrent.SmoothRateLimiter
private void resync(long nowMicros) {
    // if nextFreeTicket is in the past, resync to now
    if (nowMicros > nextFreeTicketMicros) {
      storedPermits = min(maxPermits,
          storedPermits + (nowMicros - nextFreeTicketMicros) / stableIntervalMicros);
      nextFreeTicketMicros = nowMicros;
    }
}

       实现代码如下:

      

public boolean access(String userId) {

    String key = genKey(userId);

    try (Jedis jedis = jedisPool.getResource()) {
        Map<String, String> counter = jedis.hgetAll(key);

        if (counter.size() == 0) {
            TokenBucket tokenBucket = new TokenBucket(System.currentTimeMillis(), limit - 1);
            jedis.hmset(key, tokenBucket.toHash());
            return true;
        } else {
            TokenBucket tokenBucket = TokenBucket.fromHash(counter);

            long lastRefillTime = tokenBucket.getLastRefillTime();
            long refillTime = System.currentTimeMillis();
            long intervalSinceLast = refillTime - lastRefillTime;

            long currentTokensRemaining;
            if (intervalSinceLast > intervalInMills) {
                currentTokensRemaining = limit;
            } else {
                long grantedTokens = (long) (intervalSinceLast / intervalPerPermit);
                System.out.println(grantedTokens);
                currentTokensRemaining = Math.min(grantedTokens + tokenBucket.getTokensRemaining(), limit);
            }

            tokenBucket.setLastRefillTime(refillTime);
            assert currentTokensRemaining >= 0;
            if (currentTokensRemaining == 0) {
                tokenBucket.setTokensRemaining(currentTokensRemaining);
                jedis.hmset(key, tokenBucket.toHash());
                return false;
            } else {
                tokenBucket.setTokensRemaining(currentTokensRemaining - 1);
                jedis.hmset(key, tokenBucket.toHash());
                return true;
            }
        }
    }
}


     上面的方法是最初的实现方法,对于每一个 Token Bucket,在 Redis 上面,使用一个 Hash 进行表示,一个 Token Bucket 有 lastRefillTime 表示最后一次补充 Token 的时间,tokensRemaining 则表示 Bucket 中的剩余 Token 数量,access() 方法大致的步骤为:

  1. 当一个请求 Token进入 access() 方法后,先计算计算该请求的 Token Bucket 的 key;
  2. 如果这个 Token Bucket 在 Redis 中不存在,那么就新建一个 Token Bucket,然后设置该 Bucket 的 Token 数量为最大值减一(去掉了这次请求获取的 Token)。 在初始化 Token Bucket 的时候将 Token 数量设置为最大值这一点在后面还有讨论;
  3. 如果这个 Token Bucket 在 Redis 中存在,而且其上一次加入 Token 的时间到现在时间的时间间隔大于 Token Bucket 的 interval,那么也将 Bucket 的 Token 值重置为最大值减一;
  4. 如果 Token Bucket 上次加入 Token 的时间到现在时间的时间间隔没有大于 interval,那么就计算这次需要补充的 Token 数量,将补充过后的 Token 数量更新到 Token Bucket 中。

参考资料:
    1. https://zhuanlan.zhihu.com/p/20872901?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
  1. Token Bucket
  2. Redis Incr
  3. Redis Eval
  4. Better Rate Limiting With Redis Sorted Sets
  5. Intro to rate limiting with Redis part1 andpart2
  6. Guava RateLimiter andGuava SmoothRateLimiter, 特别推荐 SmoothRateLimiter 中的文档部分
  7. Lua Reference,redis 中使用 lua 5.1
  8. Single Responsibility Principle
  9. High Cohesion, Loose Coupling
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

API调用次数限制实现 的相关文章

  • 如何从 Web 应用程序访问仅限身份验证的 Twitter API 方法

    我有一个 iPhone 网络应用程序 它最终将在 PhoneGap 应用程序中运行 但现在我在 Safari 中运行它 该应用程序需要访问 Twitter 好友的推文 包括私人推文 所以我使用 Scribe 库实现了 OAuth 我成功地将
  • 获取 Youtube 上的游戏直播列表

    我正在尝试使用 Youtube 数据 API 来获取当前与游戏相关的直播流列表 但我找不到任何符合我需要的端点并返回每个频道的观看者数量 你们知道我该如何做到这一点吗 Thanks 游戏直播列表 videoCategoryId 20 是 游
  • PyCurl 请求在执行时无限挂起

    我编写了一个脚本来从 Qualys 获取扫描结果 每周运行一次以收集指标 该脚本的第一部分涉及获取过去一周运行的每个扫描的引用列表以进行进一步处理 问题是 虽然有时这会完美地工作 但有时脚本会挂在c perform 线 手动运行脚本时这是可
  • 如何安装适用于 Windows C++ 的最新版本 OpenGL?

    我正在使用 Visual Studio 2010 运行 Windows 7 包含的 OpenGL 版本 include 是版本 1 1 我希望使用合理的当前版本 某种版本 3 或 4 我需要做什么才能达到该状态 OpenGL SDK 页面位
  • GitHub v3 API - 如何在存储库中创建初始提交?

    我正在使用 v3 API 并设法列出存储库 树 分支 访问文件内容并创建 blob 树 提交 我现在正在尝试创建一个新的存储库 并设法使用 POST user repos 来完成它 但是当我尝试在这个新存储库中创建 blob trees c
  • 我们可以使用 axios 的 onDownloadProgress 来加载 API 吗?

    我需要使用 axios 创建一个用于在 React 项目中加载 API 的进度条 我为此发现了 onDownloadProgress 函数 但我不知道我们是否可以使用它来获取诸如加载百分比之类的信息 或者它是否仅用于文件下载 所以我不确定我
  • 使用 PHP 发布到 Blogger

    我在使用 PHP 的 Blogger API 时遇到问题 我需要的是能够将新的博客文章发布到我的博客帐户 我使用的代码取自 Google API 页面 http code google com intl nl apis blogger do
  • Android 添加新日历

    我已经检查了所有从 Android 应用程序中创建新日历的方法 我见过的唯一方法是在最新的 api 版本中使用新的 Calendar API 但这似乎只有在您使用时才有效CalendarContract ACCOUNT TYPE LOCAL
  • useState 由于某种原因没有更新?

    当我尝试使用 axios 从后端 API 获取一些数据 并在由于某种原因获得结果后设置状态时 状态不会更新 当我尝试使用状态时 它只会向我显示一个空数组 但有趣的是当我console log res data 它会毫无问题地向我显示我的列表
  • Magento 2 REST API 客户自定义属性

    Magento 2 REST API 文档解释了在更新或创建客户时设置custom attributes 的方法 http devdocs magento com swagger index 20 html http devdocs mag
  • Swift:转义闭包捕获非转义参数“onCompletion”

    我的 swift 有问题 我正在尝试发送 API 请求 然后检索数据 但收到以下错误消息 Swift 转义闭包捕获非转义参数 onCompletion 有谁知道我该如何解决这个问题 提前致谢 Code class RestApiManage
  • 如何在 Google Maps API 中指示语言?

    就像你访问一样maps google com tw or maps google co kr or maps google co jp 您可以看到每个国家 地区都显示自己的语言 我可以在 Google 地图 API 中使用任何属性来动态设置
  • 适用于 iPhone 和 HTTP 直播的实时视频聊天

    所以一般来说 我想为 iPhone 制作一个具有视频聊天功能的应用程序 但经过多次搜索 我仍然找不到任何成功的结果 是否有任何公共或私有 API 可用于在 iPhone 上执行此操作 如果您的答案是 是 请帮助我 基本上 我想要的是读取连接
  • Google Sheets API v4 和 valueInputOption

    我的电子表格中有三列 第一个是日期 第二个和第三个是简单字符串 当我批量上传数据时valueInputOption RAW 我的日期列得到错误的结果 所有日期前面都有一个看不见的撇号 字符串列没问题 当我使用valueInputOption
  • 415 不支持的媒体类型; Angular2 到 API

    我是 Angular 2 的新手 我面临着一个无法找到解决方案的问题 当我尝试从 Angular 2 发布到 API 时 我得到 415 不支持的媒体类型 角度2代码 onSubmit value any console log value
  • 如何在flutter项目中使用http拦截器?

    我必须向我的所有 Api 添加标头 有人告诉我为此使用 http 拦截器 但我无法理解如何做到这一点 因为我是颤振的新手 谁能帮我举个例子吗 您可以使用http 拦截器 https pub dev packages http interce
  • JW Player javaScript API 不工作

    我使用 jwplayer version 5 10 2295 和浏览器 chrome 25 My code jwplayer container setup file path width 300px height 100px autost
  • 使用 PHP 和 OAuth 访问 SkyDrive

    我想使用 PHP 访问 skyDrive 我想检索文件和文件夹列表 下载 上传和删除文件 我有一个 microsoft dev clientID 和 clientSecret 有人可以帮助我开始使用 OAuth 连接到 skyDrive 并
  • 注册期间现有电子邮件的 422 或 409 状态代码

    我正在构建 RESTful API 遇到了一种情况 在用户注册期间 如果电子邮件已存在 则在422 and 409哪个http响应代码有意义 我浏览过类似的one https stackoverflow com questions 9269
  • 是否有 .NET 库或 API 可以与 IIS 配置数据库交互/编辑它?

    或者我是否坚持使用自己的 XML 切割 功能 我想创建一个小型任务托盘应用程序 以便我可以快速将虚拟目录重新指向硬盘上的几个文件夹之一 一点背景 我的开发机器上的代码库有 3 个不同的 svn 分支 Current Production B

随机推荐

  • 毕业后就是程序员——我的阿里、金山、中华、腾讯、360、网易面试总结(三)

    接着上篇 xff0c 这篇侧重于具体的笔试 面试的问题 xff0c 至于是哪个公司的就不去追究了 xff0c 但一定是经常问到的 xff0c 而且我嵌入式系统工程师和移动开发工程师都参与了 xff0c 问题要区别看待 xff0c 那么自然就
  • 你投资的那些EOS“侧链”都还好么?

    EOS侧链BOS的启动声势浩大 xff0c 引得一众EOS超级节点与公司参与支持 xff0c 也招来币圈大佬老猫的质疑 xff0c 直言 熊市 xff0c 我劝你善良 xff1b 近几日 xff0c 曾被称作是EOS上首条侧链的FIBOS开
  • 销毁3417万枚EOS与被刺激的EOS价格——理解增发与销毁机制

    eosio saving帐号销毁的3417万枚EOS与REX锁住的6000多万枚EOS xff0c 都刺激不了EOS价格 xff0c 甚至仍有下跌的趋势 xff0c 而几天后一觉醒来 xff0c 不管BTC还是EOS都在疯涨 xff0c 过
  • Error executing aapt: Return code -1073741819

    总会有让人喷血的事情勾起写博客的欲望 xff0c 希望能坚持 折腾了两天的是个小问题 xff0c 就是标题上的Error executing aapt Return code 1073741819 解决的方法也很简单参考1中所述 xff0c
  • EOS的危险信号——记主网上线一周年后

    市值已经跌落到第八 xff0c 除了期待即将推出的社交应用 Voice 能给 EOS 注入新的活力外 xff0c 我已经很难找到 EOS 上的新热点 xff0c 而一些危险的信号 xff0c 可能对 EOS 的发展带来负面的影响 xff0c
  • oracle官网下载JDK速度很慢的解决方案

    华为有个镜像可以下载 xff1a https repo huaweicloud com java jdk
  • 被远程连接的windows一定要设置账号的密码吗

    背景 我们有个windows系统 xff08 方便表述叫P xff0c 比如win10的 xff0c 平时使用的用户名是a xff0c 没有登录密码 xff0c 平时使用P的过程是允许没有密码登录操作系统的 但是 xff0c 如果使用另外一
  • 详解Java线程池参数

    详解Java线程池参数 目前线程池的类一般使用 spring的 xff1a org springframework scheduling concurrent ThreadPoolTaskExecutorJDK的 xff1a java ut
  • python watchdog:监控文件系统事件的Python库

    python watchdog xff1a 监控文件系统事件的Python库和shell工具 watchdog用来监控指定目录 文件的变化 xff0c 如添加删除文件或目录 修改文件内容 重命名文件或目录等 xff0c 每种变化都会产生一个
  • IDEA 使用本地maven仓库 从下载到配置

    记录一下使用过程 环境 xff1a win10 maven3 6 3 idea2019 三个分类 xff0c 可以熟悉的可以跳着看 躺着看都行 一 下载maven 1 下载maven的压缩包 我这里使用的是apache maven 3 6
  • 环形缓冲区(ring buffer),环形队列(ring queue) 原理

    环形缓冲区 ring buffer xff0c 环形队列 ring queue 多用于2个线程之间传递数据 xff0c 是标准的先入先出 FIFO 模型 一般来说 xff0c 对于多线程共享数据 xff0c 需要使用mutex来同步 xff
  • jupyter notebook找不到tensorflow,已解决

    问题描述 xff1a 在debain里安装了jupyter xff0c 安装方法参考https blog csdn net wacebb article details 117407106 spm 61 1001 2014 3001 550
  • Ubuntu删除root密码

    想让root恢复成初始时候一样 xff1a 没有密码 xff1b 切换root账号输入密码错误 xff1b 使用下面的命令 xff1a 删除root账号密码 sudo passwd d root 锁定root账号 sudo passwd l
  • 指针+1的问题

    指针 43 1 并不是指针代表的地址值 43 1 指针变量加1 xff0c 即向后移动1 个位置表示指针变量指向下一个数据元素的首地址 而不是在原地址基础上加1 至于真实的地址加了多少 xff0c 要看原来指针指向的数据类型是什么 char
  • 手把手带你学习Spring框架

    一 介绍Spring框架 1 1学习Spring框架的原因 企业级应用是指那些为商业组织 xff0c 大型企业而创建并部署的解决方案 这些大型企业级应用的结构复杂 xff0c 涉及的外部资源众多 xff0c 事务密集 xff0c 数据规模大
  • Typora+Gitee+PicGo实现markdown图片自动插入

    Typora 43 Gitee 43 PicGo实现markdown图片自动插入 文章目录 Typora 43 Gitee 43 PicGo实现markdown图片自动插入写到前面效果展示搭建步骤下载并安装Picgo安装node js创建g
  • 关于泛型的类型擦除

    关于泛型的类型擦除 什么是泛型 xff1f 在探讨类型擦除之前 xff0c 我们还是先了解一下 xff0c 泛型的概念 泛型是为了参数化类型 xff0c 定义方法时传入形参 xff0c 而调用方法时使用形参 xff0c 参数化类型就是由确定
  • AndroidRuntime java.lang.AbstractMethodError解决方案

    AndroidRuntime java lang AbstractMethodError解决方案 背景介绍 今天同事尝试编译apk的release版本 xff0c 编译成功 xff0c 但是运行时 xff0c 却爆出这个运行时异常 xff0
  • MVI是噱头还是真有用?

    MVI 是噱头还是真有用 xff1f 今天逛技术论坛的时候偶然发现了这个词 xff0c 眼前一亮 xff0c 因为之前活跃在大众面前的架构模式是MVC MVP MVVM xff0c 至于MVI真的是第一次看到 xff0c 心里一慌 xff0
  • API调用次数限制实现

    API调用次数限制实现 在开发接口服务器的过程中 xff0c 为了防止客户端对于接口的滥用 xff0c 保护服务器的资源 xff0c 通常来说我们会对于服务器上的各种接口进行调用次数的限制 比如对于某个 用户 xff0c 他在一个时间段 x