ratelimit限流器

限流器在项目中的应用

Posted by Liangjf on July 20, 2020

限流器在项目中的应用

mos项目(盒子/投影仪)应用到限流器的接口

  • 1.广告列表接口[/v2/mos/adlist]
  • 2.获取应用推荐列表[/v2/mos/app_recommend]
  • 3.获取屏保的列表v2.0[/v2/mos/screens]

都是使用redis实现的原子计数法限流(集群计数限流), 分布式流控不可用时(例如缓存挂掉), 切到单机流控

目的是这三个接口都是数据量大的接口, 过渡频繁的请求会严重占用带宽, 造成不必要的浪费。 通过流量控制来限制请求, 会节省资源, 和保护后台系统不被击垮

限流可以应对

  • 1.热点业务带来的突发请求
  • 2.调用方 bug 导致的突发请求
  • 3.恶意攻击请求

限流算法

  • 1.原子计数法
  • 2.滑动窗口算法
  • 3.令牌桶算法
  • 4.木桶算法

1.原子计数法

出现”突刺现象”, 比例1秒限流1000, 前10ms有10个, 第990ms有990个请求 不能防止临界点的突发请求. 可能极端情况下被人利用临界点前后一刻的合法流量请求, 造成瞬间的时间段的双倍请求, 击垮后台服务 究其原因是原子计数法只是一个时间窗口, 不能很好的平滑请求. 原子计数法是特殊的滑动窗口算法, 只是滑动窗口只是单独一个(时间段)

2.滑动窗口算法

把大的时间段划分为n个小段的时间窗口, 根据时间窗口的各个小段的累加来限流, 可以平滑的限流

3.令牌桶算法

重点是放操作的限流, 通过队列+生产令牌线程组成, 生产线程根据设定的限流大小的速度来生产令牌, 并投入到队列中, 消费端仅仅是关注队列是否有令牌, 有就允许通过, 没有就限制通过. 生产令牌和消费令牌解耦. 可以通过设定限流大小来调整生产令牌的速度, 所以可以灵活的调整生产令牌的速度, 应对突然流量

4.木桶算法

重点是取操作的限流, 通过固定大小队列+消费请求线程. 请求进入队列, 若队列已满就丢弃请求或者暂存起来, 若队列未满就push进队列; 有个消费线程以恒定的速度从队列中取请求, 若没有就睡眠 可以防止出现”突刺现象”, 防止突发流量, 能够使请求以恒定的速度被消费; 但是无法应对短时间的突发流量

5.集群限流

限制某个资源被每个用户或者商户的访问次数,5s只能访问2次,或者一天只能调用1000次,这种需求,单机限流是无法实现的,这时就需要通过集群限流进行实现

思路: 每次有相关操作的时候,就向redis服务器发送一个incr命令,比如需要限制某个用户访问/index接口的次数,只需要拼接用户id和接口名生成redis的key,每次该用户访问此接口时,只需要对这个key执行incr命令,在这个key带上过期时间,就可以实现指定时间的访问频率

拓展知识

Hystrix熔断, 降级

先说一种比较low的做法,但是也挺实际的一般做法有:

服务码+配置中心

调用任何服务时都传入必要参数服务码和开关,默认关闭,当触发某种条件时可打开开关,或者通过配置中心手动推送开关新的值,从而保护系统不被单个服务压垮

其中开源项目中比较著名的是Hystrix熔断, 降级

Hystrix引入以下手段来保护系统

  • 资源隔离(线程池和信号量两种手段的隔离)(Hystrix-go,线程隔离,每个执行是一个goroutine)
  • 限流
  • 降级
  • 熔断(断路器)

Hystrix如何设计实现这些手段呢?

  • 使用命令模式将所有对外部服务(或依赖关系)的调用包装在HystrixCommand或HystrixObservableCommand对象中,并将该对象放在单独的线程中执行
  • 每一个依赖都有自己对应的线程池或者信号量,线程池耗尽时,拒绝请求
  • 维护请求的各种状态(成功,失败,超时的次数)
  • 当错误率到达一定阈值时,进行熔断,过一定的时间后又恢复
  • 提供降级,失败,成功,熔断后的回调逻辑
  • 实时的监控指标和配置信息的修改

Hystrix的步骤

  • 1:每次调用创建一个新的HystrixCommand,把依赖调用封装在run()方法中。
  • 2:执行execute()/queue做同步或异步调用。
  • 3:判断熔断器(circuit-breaker)是否打开,如果打开跳到步骤8,进行降级策略,如果关闭进入步骤。
  • 4:判断线程池/队列/信号量是否跑满,如果跑满进入降级步骤8,否则继续后续步骤。
  • 5:调用HystrixCommand的run方法。运行依赖逻辑
  • 5a:依赖逻辑调用超时,进入步骤8。
  • 6:判断逻辑是否调用成功
  • 6a:返回成功调用结果
  • 6b:调用出错,进入步骤8。
  • 7:计算熔断器状态,所有的运行状态(成功, 失败, 拒绝,超时)上报给熔断器,用于统计从而判断熔断器状态。
  • 8:getFallback()降级逻辑。
    • 以下四种情况将触发getFallback调用:
      • (1):run()方法抛出非HystrixBadRequestException异常
      • (2):run()方法调用超时
      • (3):熔断器开启拦截调用
      • (4):线程池/队列/信号量是否跑满
  • 8a:没有实现getFallback的Command将直接抛出异常
  • 8b:fallback降级逻辑调用成功直接返回
  • 8c:降级逻辑调用失败抛出异常
  • 9:返回执行成功结果