利用Redis对含有分页参数的API接口做调用次数限制

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Redis 版,经济版 1GB 1个月
简介: 利用Redis对含有分页参数的API接口做调用次数限制

 背景 - 限制接口调用次数:

       提供内部调用的全量拉取数据接口,以用户appid作为维度区分,一定时间段内(比如1小时),查询某一接口的appid和limit、offset等组合参数只能查询一次,多次查询相同组合参数则返回错误值。

思路:

       采用string类型,key就是模块名:api_limiter_xxx(根据需要自定义key名称),value就是当前可以访问的页数(每次访问成功都自增1)。第一次访问某接口时,若key不存在,那么就添加;若存在,则获取当前redis中对应key的"页数值",并与入参Limit和Offset计算后的页数结果进行比较,若相等则调用成功,反之调用失败。

其中,在设置key和value的时候也要设置"过期时间"。所以关键在于 SETNX 的使用,若存在,则匹配计算的页数与redis中的计数器页数是否相等,若不存在,则设置kv。

注意:其中每次set key的值的时候,过期时间的处理。这里在set之前先通过TTL获取key剩余的过期时间,然后set时再带入接下来剩余的时间即可,避免每次都重新设置固定的过期时间。

    1. 初始时,redisPageCnt = 1
    2. 首次调用接口时,Limit=10,Offset=0,calculateCnt = (Limit + Offset) / Limit = 1;redisPageCnt=1
    3. 此时 calculateCnt = redisPageCnt 相等,redisPageCnt++,redisPageCnt = 2
    4. 再次调用接口时,Limit=10,Offset=10,calculateCnt = (Limit + Offset) / Limit = 2;redisPageCnt=2
    5. 此时 calculateCnt = redisPageCnt 相等,redisPageCnt++,redisPageCnt = 3
    6. ...

           这样的话就可以按页数从前往后递增,每页在限制的超时时间内(1小时)访问一次。在key的超时时间内,访问过的页数也不能再次访问,除非redis中的key超时时间失效。

    请求示例:

    var req = TestReq{ Appid: 1256299843, Limit: 10, Offset: 20, }

    packagemainimport (
    "fmt""strconv""time""github.com/go-redis/redis")
    // 限流:针对含有分页参数的接口,对于不同"Limit和Offset"组合做限制,避免某个用户大量请求影响后台服务处理其他正常请求// 示例://  var req = TestReq{//      Appid:  1256299843,//      Limit:  10,//      Offset: 20,//  }typeTestReqstruct {
    Appiduint64LimitintOffsetint}
    funcAPILimiter(reqTestReq) (errerror) {
    ifreq.Limit!=10 { // 校验入参,暂时写死,根据情况修改fmt.Println("req.Limit format err: ")
    // return response ...return    }
    redisCli :=redis.NewClient(&redis.Options{
    Addr:     "xxx.xxx.xxx.xxx:3306", // ip:portPassword: "yundingyd",
    PoolSize: 10,
        })
    // pong, err := redisCli.Ping().Result()// 不存在 -> 初始化;存在 -> 不处理redisKey :="api_limiter_"+strconv.FormatUint(req.Appid, 10)
    isOk, err :=redisCli.SetNX(redisKey, 1, time.Hour).Result() // 限制超时1小时,Result()针对不同命令自动转换对应结果iferr!=nil&&err!=redis.Nil&&isOk==false {
    fmt.Println("APILimiter SetNX err: ", err)
    // return response ...return    }
    calculatePageCnt := (req.Limit+req.Offset) /req.LimitredisPageCnt, err :=redisCli.Get(redisKey).Int()
    iferr!=nil&&err!=redis.Nil&&isOk==false {
    fmt.Println("APILimiter redisCli.Get err: ", err)
    // return response ...return    }
    ifcalculatePageCnt!=redisPageCnt {
    fmt.Printf("接口调用失败:当前Limit和Offset对应的页数无效: limit=%d, offset=%d, calculateCnt=%d, redisPageCnt=%d\n", req.Limit, req.Offset, calculatePageCnt, redisPageCnt)
    // return response ...return    }
    deferfunc() {
    iferr==nil { // 具体的业务逻辑,期间可能会产生err,只有err为空时才执行以下逻辑~// 先查询当前key的剩余过期时间timeDuration, err :=redisCli.TTL(redisKey).Result()
    iferr!=nil&&err!=redis.Nil {
    fmt.Println("redisCli.TTL err: ", err)
    // return response ...return            }
    // 再设置当前key的剩余过期时间isOk, err :=redisCli.Set(redisKey, redisPageCnt+1, timeDuration).Result() // page + 1iferr!=nil&&err!=redis.Nil&&isOk!="OK" {
    fmt.Println("redisCli.Set err: ", err)
    // return response ...return            }
    fmt.Println("接口调用成功!!!")
            }
        }()
    // 具体的业务逻辑(期间可能会产生err,除非期间err为nil,否则defer函数不执行 page+1 等操作):// ...return}
    funcmain() {
    varreq=TestReq{
    Appid:  1256299843,
    Limit:  10,
    Offset: 20,
        }
    err :=APILimiter(req)
    iferr!=nil {
    fmt.Println("APILimiter err: ", err)
    return    }
    }

    image.gif


    相关实践学习
    基于Redis实现在线游戏积分排行榜
    本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
    云数据库 Redis 版使用教程
    云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
    目录
    相关文章
    |
    4天前
    |
    NoSQL Java Redis
    redis连接池参数
    如果系统启动完马上就会有很多的请求过来,那么可以给redis连接池做预热,比如快速的创建一些redis连接,执行简单命令,类似ping(),快速的将连接池里的空闲连接提升到minldle的数量。
    14 0
    |
    1天前
    |
    JSON 安全 API
    如何高效编写API接口:以Python与Flask为例
    构建RESTful API的简明教程:使用Python的Flask框架,从环境准备(安装Python,设置虚拟环境,安装Flask)到编写首个API(包括获取用户列表和单个用户信息的路由)。运行API服务器并测试在`http://127.0.0.1:5000/users`。进阶话题包括安全、数据库集成、API文档生成和性能优化。【6月更文挑战第27天】
    17 7
    |
    1天前
    |
    JSON 安全 API
    实战指南:使用PHP构建高性能API接口服务端
    构建RESTful API的简要指南:使用PHP和Laravel,先安装Laravel并配置数据库,接着在`api.php`中定义资源路由,创建`PostController`处理CRUD操作,定义`Post`模型与数据库交互。使用Postman测试API功能,如创建文章。别忘了关注安全性、错误处理和性能优化。
    12 2
    |
    3天前
    |
    人工智能 运维 Serverless
    函数计算产品使用问题之启动的实例是否有调用api接口停止功能
    函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
    |
    4天前
    |
    数据挖掘 API 开发者
    ​Email API有哪些,最好的3个API接口有哪些
    Email API如SendGrid、Mailgun和AOKSend是企业自动化邮件通信的关键工具。它们提供邮件发送、接收和管理功能,提升效率,优化客户体验。SendGrid以其高可靠性、强大分析和易于集成备受青睐;Mailgun以灵活性和高发送率著称;而AOKSend则以其高效、详细分析和易用性脱颖而出。通过使用这些API,企业能实现定制化邮件服务,跟踪性能,提升邮件营销效果。
    |
    4天前
    |
    API
    个人微信api接口源代码
    个人微信api接口源代码
    |
    8天前
    |
    自然语言处理 安全 API
    触发邮件接口有哪些?邮件API文档
    **触发邮件接口**如AokSend、Mailgun、Amazon SES、Postmark和Sendinblue是自动化企业通信的关键。这些接口在特定事件时自动发送邮件,提高效率和客户体验。例如,AokSend提供详细的API文档,支持事件触发、模板管理和多语言集成;Mailgun以灵活性著称;Amazon SES适合大规模发送;Postmark专注于事务邮件;Sendinblue则提供邮件、短信和营销自动化功能。每种服务都有示例代码展示如何使用API发送邮件。选择合适的接口能提升企业通信效率和客户满意度。
    |
    8天前
    |
    安全 API 网络安全
    API接口安全加固:应对黑客攻击的实战指南
    **API安全摘要:** API成为黑客目标,攻击类型包括未授权访问、CSRF、DDoS、数据泄露和注入攻击。防御策略包括使用OAuth 2.0和JWT进行认证授权,防止CSRF攻击,限制请求速率,避免数据泄露,以及实施注入攻击防护。开发者应定期更新安全措施,确保API安全性。示例代码展示了Node.js中JWT认证的实现。
    |
    2天前
    |
    敏捷开发 测试技术 API
    阿里云云效产品使用问题之API中包含有获取测试计划的接口吗
    云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。
    |
    2天前
    |
    XML JSON 程序员
    程序员必知:常用天气预报API接口整理(转)
    程序员必知:常用天气预报API接口整理(转)