网关限流功能性能优化

· 浏览次数 : 2

小编点评

本文将从设计与原理方面分享优化过程中的思考,不涉及具体的代码实现。 一、限流策略概述 限流策略包含多种策略,如根据用户维度限流、IP维度限流、接口维度限流等。每种限流策略维护自己的计数,当触发限流拒绝时会拒绝请求。限流策略会设置好总允许的权重值,通过Redis的Lua脚本进行权重的扣除和增加,直到权重为0。 二、限流方案的优化 1. 减少Redis访问次数 多线程并行访问并行可以同时进行网络请求,减少总时间。串行改并行会改动较小,但本质上没有减少资源消耗。 2. 批量访问Redis 批量访问可以减少IO耗时,但在Redis执行重复操作上并没有减少,总体来看可以减少总耗时。 3. 数据保存与中心化 使用Redis作为中心化数据保存,利用其原子性保证权重加减的正确性。内存中保存部分权重数据,在扣除完毕后,再行至Redis扣除。 4. 预知Redis权重状态 利用令牌桶算法特点,在上一次权重为0时,可预测后序请求触发限流的可能性。若判断权重持续为0,则跳过Redis操作。 三、优化后的限流方案 1. 优点: - 减少Redis访问次数 - 提高性能 - 避免恶意流量打垮Redis 2. 缺点: - 限流精确度降低 - 限流边界值变得模糊 3. 优化后的效果: - Redis CPU使用率大幅降低 - 机器CPU使用率明显下降

正文

本文主要从设计与原理方面分享优化过程中的思考,不涉及具体的代码实现。在分析过程中我会写一些当时思考的问题,在看后续答案时可以自己也先思考一下

老的限流方案

首先讲解一下原本网关限流功能的实现方案,省略其中的白名单,黑名单,令牌桶算法实现等一些细节
  • 限流策略中包含多种策略,比如根据用户维度限流,ip维度限流,接口维度限流等,每种限流策略各自维护自己的计数。任意一个策略触发限流拒绝都会拒绝请求
  • 每种限流策略都会设置好总允许的权重值,每次访问通过redis的lua脚本进行权重的扣除和增加,直到为0,扣除不了的则触发拒绝
  • 简单讲一下限流使用的令牌桶,网关会在redis中记录剩余令牌数和上次补充令牌的时间点,redis的key是限流维度生成的唯一id,比如用户维度则是特殊前缀+用户id。假设我们有一个令牌桶,初始令牌数为10,每5秒钟补充5个令牌,桶的最大容量也是10个令牌。那么令牌使用情况如下图:
  • 老限流的流程图:

限流存在的问题

总体来看,这个限流的方案还是比较简单的,当流量不大的时候也可以正常运行。
但是流量大的时候存在一些问题,大家可以先想一下有哪些问题?

  1. 一次请求由于需要从多个维度进行限流,所以会串行的多次访问redis,每次io都需要消耗时间
  2. lua脚本在redis中执行,redis的性能成为瓶颈,一方面是cpu高,另一方面redis的命令也是串行的,后面的命令需要等待前面的命令
  3. 即使权重已经扣除到0了,当请求过来时,仍然需要访问redis去判断是否可以通过

分析和优化思路

针对这3个问题,我们分析一下,都有哪些处理方案,以及这些方案的优缺点

第一个问题:多次串行IO
  1. 多线程并行访问

    • 并行可以同时进行网络请求,虽然总次数没有少,但是总时间可以减少到最慢的那次请求的耗时。比如,每次1秒,串行访问3次,总耗时3秒,改为并行则只需要1秒。
    • 串行改并行一般来说改动起来会小一些,比较好实现。可以节省时间。但是本质上没有减少资源消耗
    • 同时由于本次请求的是redis,redis仍然需要进行串行执行
    • 所以这个方案并不好
  2. 合并请求批量访问

    • 在这个场景中,时间消耗分为,io耗时+redis执行耗时。
    • 批量访问可以减少io耗时的部分,但是不会减少redis执行耗时。
    • 总体来看这个方案可以减少总耗时,但是由于需要聚合请求,分发响应,因此代码改动会稍微大一些。
    • 目前看是一个可行的方案
  3. 减少redis的访问

    • 首先我们要分析redis的作用是什么,为什么我们需要访问redis?
      • 由于网关是无状态的,限流是全局维度的,所以针对这个场景我们利用redis作为一个中心化的数据保存,也就是每个策略的权重数据。其次由于请求是并发的,所以我们利用了redis来保证权重加减的原子性。在这2个原因中的3个关键点是:数据保存,中心化和原子性。
    • 数据保存通常来讲我们遵循(内存>redis>数据库)。那我们是否可以用内存代替redis实现数据保存?
      • 如果网关只有一台机器,那么是可行的,但是现在由于多台机器,所以中心化这个关键点限制了我们把数据从redis改为内存。
    • 那我们改进一下这个思路,能不能把部分数据改为内存?
      • 数据是权重数据,本质上就是一个计数器。那我们将计数器的一部分值放到内存中,扣除完了之后再来redis扣除一次(这个思路其实就是java中的TLAB机制的启发)。
    • 数据保存和中心化都没问题了,那原子性是否可以保证?
      • 不同机器从redis中扣除这部分逻辑与之前是一致的,所以原子性没问题,保存在内存中的值,只会被单机操作一定可以保证原子性(最差就是加锁)。
    • 那么最终分析之后这个方案也是可行的
第二个问题: redis执行lua脚本成为瓶颈
  1. 优化lua脚本
    • lua脚本实现的是令牌桶算法,在当前场景下没有发现优化空间,因此不考虑
  2. redis分片处理
    • 不同的key路由到不同的redis中,可以解决瓶颈问题,方案可行,但是本质上没有减少资源消耗
  3. 减少redis访问
    • 同上一个问题的解决方案,方案可行
第三个问题:权重扣除为0,仍然访问redis
  1. 还是先分析原因,为什么权重为0时我们需要访问redis?
    • 因为内存中并不知道权重是否已经为0,这个数据只在redis中存在。
  2. 那我们有什么办法可以提前知道redis中权重是否为0?
    • 好像没有办法
  3. 但是如果上一次请求返回了权重为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%

优化后的总结

  • 优化的首要关键因素是能发现问题,也就是优化点
    • 一般最直接的方式就是通过监控发现,所以监控很重要
    • 业务的理解程度也很大程度上会影响你是否能发现问题
  • 其次是问题的解决方案
    • 解决方案跟个人经验有关系,但是大部分的问题的解决方案都是类似的,通过其他人的问题解决方案来积累经验我认为是最值的
    • 一般来讲很难一下子就想到最优方案。想出一种方案后最好再思考一下,有哪些缺点,还有没有其他方案。比较多个方案选一个相对最优的方案
  • 优化时常用的一些思路
    • 通用方案不一定是最好的,可以利用一些功能的特点去针对性的优化
    • 优化到一定程度后,一般很难做到十全十美,通过放弃一部分东西可以换取一些你更需要的东西,比如空间换时间

如果觉得本文的内容有不足之处,欢迎指出
总觉得自己写的文章语言都好生硬,不知道怎么改善

与网关限流功能性能优化相似的内容:

网关限流功能性能优化

本文主要从设计与原理方面分享优化过程中的思考,不涉及具体的代码实现。在分析过程中我会写一些当时思考的问题,在看后续答案时可以自己也先思考一下 老的限流方案 首先讲解一下原本网关限流功能的实现方案,省略其中的白名单,黑名单,令牌桶算法实现等一些细节 限流策略中包含多种策略,比如根据用户维度限流,ip维

Higress 基于自定义插件访问 Redis

本文介绍了Higress,一个支持基于WebAssembly (WASM) 的边缘计算网关,它允许用户使用Go、C++或Rust编写插件来扩展其功能。文章特别讨论了如何利用Redis插件实现限流、缓存和会话管理等高级功能。

【转帖】【奇技淫巧】Linux | 安全保障防火墙-iptables

虽然说Linux在安全方面确实相当于windows要更加可靠一些,但一般使用其作为服务器的我们,也不能大意,也是需要严格限制网络传输过程中的出入规则。上篇文章我们有聊到统计网络的信息,这篇文章来学习一下比较著名的防火墙iptables,它已经有十几年的历史了,算是不折不扣的Linux系统的功臣。 一

SpringCloud-Gateway搭建保姆级教程

一、网关介绍 1、什么是网关? 使⽤服务⽹关作为接⼝服务的统⼀代理,前端通过⽹关完成服务的统⼀调⽤ 2、⽹关可以⼲什么? 路由:接⼝服务的统⼀代理,实现前端对接⼝服务的统⼀访问 过滤:对⽤户请求进⾏拦截、过滤(⽤户鉴权)、监控 限流:限制⽤户的访问流量 3、常⽤的⽹关 Nginx Spring Cl

微服务11:熔断、降级的Hystrix实现(附源码)

微服务1:微服务及其演进史 微服务2:微服务全景架构 微服务3:微服务拆分策略 微服务4:服务注册与发现 微服务5:服务注册与发现(实践篇) 微服务6:通信之网关 微服务7:通信之RPC 微服务8:通信之RPC实践篇(附源码) 微服务9:服务治理来保证高可用 微服务10:系统服务熔断、限流 1 介绍

Redisson 限流器源码分析

Redisson 限流器源码分析 对上篇文章网友评论给出问题进行解答:redis 的key 是否会过期,过期指的限流器 可以先阅读上篇文章: redis + AOP + 自定义注解实现接口限流 - 古渡蓝按 - 博客园 (cnblogs.com) 注解AOP 代码部分提取 // 调用Reids工具类

Redisson 限流器源码分析

Redisson 限流器源码分析 对上篇文章网友评论给出问题进行解答:redis 的key 是否会过期 可以先阅读上篇文章: redis + AOP + 自定义注解实现接口限流 - 古渡蓝按 - 博客园 (cnblogs.com) 注解AOP 代码部分提取 // 调用Reids工具类的rateLim

(性能测试)--记录一次高可用场景导致CPU资源升高

测试场景:高可用场景--限流测试; 被测交易:查询类交易,HTTP协议; 交易链路:jmeter - web - coimpre(前置服务) -- coimbp -- cobp (coimbp 、coimpre 都会访问同一个数据库); 注:cobp 为合肥机房,其他服务均为北京机房,要注意跨网段存

Docker 镜像库国内加速的几种方法

概述 在国内,拉取 Docker 镜像速度慢/时不时断线/无账号导致限流等,比较痛苦😣. 这里提供加速/优化的几种方法。 梳理一下,会碰到以下情况: 国内下载速度慢/时不时断线:是因为网络被限制了。 没有公共镜像库账号导致限流:是因为 Docker Hub 等主流镜像库,近年来纷纷开始对未登录的匿

通过mat获取OOM时对象信息的方法与过程

通过mat获取OOM时对象信息的方法与过程 背景 如果谁的耐心不好, 就让他去看MAT里的objects信息. 有项目出现了OOM的情况 我在公司这边有一台内存比较高的Win10机器. 然后帮助同事进行了dump文件的分析. 为了备忘, 这里简单总结一下. 时间总结 公司网络限速. 总结为: 下载2