【原创】网关限流功能性能优化

发布时间:2026/8/1 19:47:47
【原创】网关限流功能性能优化 本文主要从设计与原理方面分享优化过程中的思考不涉及具体的代码实现。在分析过程中我会写一些当时思考的问题在看后续答案时可以自己也先思考一下老的限流方案首先讲解一下原本网关限流功能的实现方案省略其中的白名单黑名单令牌桶算法实现等一些细节限流策略中包含多种策略比如根据用户维度限流ip维度限流接口维度限流等每种限流策略各自维护自己的计数。任意一个策略触发限流拒绝都会拒绝请求每种限流策略都会设置好总允许的权重值每次访问通过redis的lua脚本进行权重的扣除和增加直到为0扣除不了的则触发拒绝简单讲一下限流使用的令牌桶网关会在redis中记录剩余令牌数和上次补充令牌的时间点redis的key是限流维度生成的唯一id比如用户维度则是特殊前缀用户id。假设我们有一个令牌桶初始令牌数为10每5秒钟补充5个令牌桶的最大容量也是10个令牌。那么令牌使用情况如下图老限流的流程图限流存在的问题总体来看这个限流的方案还是比较简单的当流量不大的时候也可以正常运行。但是流量大的时候存在一些问题大家可以先想一下有哪些问题一次请求由于需要从多个维度进行限流所以会串行的多次访问redis每次io都需要消耗时间lua脚本在redis中执行redis的性能成为瓶颈一方面是cpu高另一方面redis的命令也是串行的后面的命令需要等待前面的命令即使权重已经扣除到0了当请求过来时仍然需要访问redis去判断是否可以通过分析和优化思路针对这3个问题我们分析一下都有哪些处理方案以及这些方案的优缺点第一个问题多次串行IO多线程并行访问并行可以同时进行网络请求虽然总次数没有少但是总时间可以减少到最慢的那次请求的耗时。比如每次1秒串行访问3次总耗时3秒改为并行则只需要1秒。串行改并行一般来说改动起来会小一些比较好实现。可以节省时间。但是本质上没有减少资源消耗同时由于本次请求的是redisredis仍然需要进行串行执行所以这个方案并不好合并请求批量访问在这个场景中时间消耗分为io耗时redis执行耗时。批量访问可以减少io耗时的部分但是不会减少redis执行耗时。总体来看这个方案可以减少总耗时但是由于需要聚合请求分发响应因此代码改动会稍微大一些。目前看是一个可行的方案减少redis的访问首先我们要分析redis的作用是什么为什么我们需要访问redis由于网关是无状态的限流是全局维度的所以针对这个场景我们利用redis作为一个中心化的数据保存也就是每个策略的权重数据。其次由于请求是并发的所以我们利用了redis来保证权重加减的原子性。在这2个原因中的3个关键点是数据保存中心化和原子性。数据保存通常来讲我们遵循(内存redis数据库)。那我们是否可以用内存代替redis实现数据保存如果网关只有一台机器那么是可行的但是现在由于多台机器所以中心化这个关键点限制了我们把数据从redis改为内存。那我们改进一下这个思路能不能把部分数据改为内存数据是权重数据本质上就是一个计数器。那我们将计数器的一部分值放到内存中扣除完了之后再来redis扣除一次这个思路其实就是java中的TLAB机制的启发。数据保存和中心化都没问题了那原子性是否可以保证不同机器从redis中扣除这部分逻辑与之前是一致的所以原子性没问题保存在内存中的值只会被单机操作一定可以保证原子性最差就是加锁。那么最终分析之后这个方案也是可行的第二个问题: redis执行lua脚本成为瓶颈优化lua脚本lua脚本实现的是令牌桶算法在当前场景下没有发现优化空间因此不考虑redis分片处理不同的key路由到不同的redis中可以解决瓶颈问题方案可行但是本质上没有减少资源消耗减少redis访问同上一个问题的解决方案方案可行第三个问题:权重扣除为0仍然访问redis还是先分析原因为什么权重为0时我们需要访问redis因为内存中并不知道权重是否已经为0这个数据只在redis中存在。那我们有什么办法可以提前知道redis中权重是否为0好像没有办法但是如果上一次请求返回了权重为0那么这一次请求是否可以判断出redis中权重是否为0答案是部分可以。利用令牌桶算法的特点令牌桶每隔一定时间会增加令牌。如果当前权重已经是0且在到需要增加令牌的时间之前权重一定一直为0。所以当上一次请求返回为0时同时记录上一次补充令牌的时间点那么就可以推算出在哪个时间点前权重一直为0那么此时就不需要再次访问redis。通过这种方式可以极大的减少权重为0时的redis访问。不管总请求数是多少在一个令牌补充周期内每台机器只会在权重为0时访问一次redis。这个问题的解决方案与上2个问题的解决方案同时兼容因此也是可行的优化后的方案现在我们总结上面的问题解决方案可以看到减少redis访问这个方案可以同时解决第一个和第二个问题并且真实的减少了资源消耗第三个问题的解决方案也不冲突因此优化后的限流方案改为如下流程新方案的优点从原本一次请求多次redis访问变成了多次请求一次redis操作大部分扣除权重不经过网络纯内存操作redis访问极大的减少redis的消耗变小权重扣完后不会再请求redis避免恶意流量打垮redis新方案有什么缺点限流的精确度变差限流的边界值被模糊了比如原本限流1w次现在变成不到1w次就已经触发了限流已经触发限流后在补充令牌前后面的请求可能又可以成功这个缺点我认为是可以接受的毕竟我们的限流没有必要那么的精确可以容忍提一个问题假设一个令牌周期内限流1w次新方案的限流最早从第多少次会触发第一次限流和哪些因素有关优化后的效果redis的cpu使用率从85% - 5%机器的cpu使用率从70% - 50%优化后的总结优化的首要关键因素是能发现问题也就是优化点一般最直接的方式就是通过监控发现所以监控很重要业务的理解程度也很大程度上会影响你是否能发现问题其次是问题的解决方案解决方案跟个人经验有关系但是大部分的问题的解决方案都是类似的通过其他人的问题解决方案来积累经验我认为是最值的一般来讲很难一下子就想到最优方案。想出一种方案后最好再思考一下有哪些缺点还有没有其他方案。比较多个方案选一个相对最优的方案优化时常用的一些思路通用方案不一定是最好的可以利用一些功能的特点去针对性的优化优化到一定程度后一般很难做到十全十美通过放弃一部分东西可以换取一些你更需要的东西比如空间换时间如果觉得本文的内容有不足之处欢迎指出推荐一个好用的二次验证码工具微信小程序【二次验证码TOTP】兼容谷歌验证码 无缝支持Google Authenticator等标准TOTP验证器通用各类平台账号云端加密备份 密钥数据端到端加密上传云端换机不丢失安全又便捷API快速集成 提供开放API轻松对接各类应用系统实现自动化验证码获取多端共享基于微信小程度可同时在手机PC端共同使用一键复制扫描二维码试用,或微信小程序搜索“二次验证码TOTP”