分布式限流方案

https://www.cnblogs.com/jiangym/p/17473049.html

https://www.cnblogs.com/jiangym/p/17471590.html

常见限流

  • 验证码
  • 通常会设置多个维度的限流规则
  • IP每秒的访问评率小于10、连接数小于5
    • (怎么实现的?)
  • 每台机器QPS最高1000,连接数最大保持200
    • 怎么实现的?
  • 整个服务器作为一个整体,设置更高的high-level限流规则
  • Nginx限流
    • 基于ID地址的限流方案
    • 基于最大连接数的限流方案
  • 基于Redis+Lua的分布式限流
    • lua
    • lua+redis
    • 注解封装,自动以注解封装限流逻辑
  • 黑白名单限流
  • 网关层限流

 

  1. 网关层限流
    1. 网关层不像改个程序代码那么简单,和运维也相关
    2. Nginx
    3. gateway
    4. zuul
  2. 中间件限流
    1. 限流下沉到业务层,开发就可以自行控制
    2. redis
  3. 限流组件
    1. sentinel 阿里开源的

 限流算法

 常见就四种

  • 令牌桶算法
  • 漏桶算法
  • 滑动窗口
  • 计数器算法

令牌桶

token bucket

目前应用最广泛

分为

  1. 令牌
    1. 匀速发放 每秒100个请求 或者每分钟50个这种
    2. 匀速和非匀速的区别?
    1. 生成之后放到桶中,到了额定容量,新的令牌就丢弃
  2. 请求获取令牌
    1. 请求获得令牌才能执行后续逻辑。
    2. 可以用缓冲队列存多余的令牌。

 

漏桶

Leaky Bucket

 数据表放桶里。桶满了数据表将被丢弃。

 

区别

根据它们各自的特点不难看出来,这两种算法都有一个“恒定”的速率和“不定”的速率。

令牌桶是以恒定速率创建令牌,但是访问请求获取令牌的速率“不定”,反正有多少令牌发多少,令牌没了就干等。

而漏桶是以“恒定”的速率处理请求,但是这些请求流入桶的速率是“不定”的。

从这两个特点来说,漏桶的天然特性决定了它不会发生突发流量,就算每秒1000个请求到来,那么它对后台服务输出的访问速率永远恒定。

而令牌桶则不同,其特性可以“预存”一定量的令牌,因此在应对突发流量的时候可以在短时间消耗所有令牌,其突发流量处理效率会比漏桶高,但是导向后台系统的压力也会相应增多。

 

具体实现

Guava RateLimiter 当前服务器限流

非阻塞 令牌桶

 非阻塞限流

同步阻塞限流

令牌不足的时候就阻塞住。

 

适用于 对单机资源敏感  读取的资源都是本地 不会有其他的组件

比如数据库,多个机器,就不适合这种的。

 

预热模型

@Override
void doSetRate(double permitsPerSecond, double stableIntervalMicros) {
      double oldMaxPermits = maxPermits;
      
    // maxPermits表示令牌桶内最大容量,它由我们设置的预热时间除以稳定间隔获得
    // 打个比方,如果stableIntervalMicros=0.1s,而我们设置的预热时间是2s
    // 那么这时候maxPermits就是2除以0.1=20
      maxPermits = warmupPeriodMicros / stableIntervalMicros;
      
      // 这句不用解释了吧,halfPermits是最大容量的一半
      halfPermits = maxPermits / 2.0;
  
      // coldIntervalMicros就是我们前面写到的3倍间隔,通过稳定间隔*3计算得出
      // 稳定间隔是0.1,3倍间隔是0.2,那么平均间隔是0.2
      double coldIntervalMicros = stableIntervalMicros * 3.0;
      
      // slope的意思是斜率,也就是前面我们图中预热阶段中画出的斜线(速率从稳定间隔向3x间隔变化的斜线)
      // 它的计算过程就是一个简单的求斜率公式
      slope = (coldIntervalMicros - stableIntervalMicros) / halfPermits;
      
      // 计算目前令牌桶的令牌个数
      if (oldMaxPermits == Double.POSITIVE_INFINITY) {
          // 如果令牌桶最大容量是无穷大,则设置当前可用令牌数为0
          // 说实话这段逻辑没什么用
        storedPermits = 0.0;
      } else {
        storedPermits = (oldMaxPermits == 0.0)
            ? maxPermits // 初始化的状态是3x间隔
            : storedPermits * maxPermits / oldMaxPermits;
  }
}

 

基于Nginx的IP限流

  1. 修改host文件
  2. 修改nginx,把host用的域名放到路由规则里 修改nginx.conf
    1. server
      1. server_name
      2. location
      3. 这里可以添加限流规则
      4.  

Nginx连接数的限制

 

 前100M不限流:

 

基于Redis+Lua的分布式限流

redis基于内存的,并且线程安全,所以适合做限流

 这种限流不是在Java内写的,比如获取令牌场景,会涉及发放令牌、查询令牌等操作,网络开销大。

所以直接通过Lua脚本方式,更好。

Lua

很小巧精致的语言,C语言编写的,源码两万行,Lua解释器才200K。

学这个语言不用面面俱到,因为细节很繁琐。

Redis中有Lua解释器,天生的原子性操作。有脚本预编译,直接调用编译好的脚本就行。

 Lua 简单语法

 结果是reject

 Redis中执行Lua

redis中执行Lua

 

Lua脚本预导入Redis

 

检查预导入的脚本是否存在

 清空预导入脚本

限流注解: 

https://www.cnblogs.com/jiangym/p/17576108.html

 

 

 

 

 

如果限流服务挂了,怎么兜底?直接放开限流

 

posted @ 2023-07-24 17:07  CodingOneTheWay  阅读(95)  评论(0编辑  收藏  举报
回到顶部