限流(限流保护器)

  本篇文章为你整理了限流(限流保护器)的详细内容,包含有限流孔板 限流保护器 限流了怎么办怎么恢复限流 限流式电气防火保护器 限流,希望能帮助你了解 限流。

  系统存在服务上限,流量超过服务上限会导致系统卡死、崩溃。
 

  限流:为了在高并发时系统稳定可用,牺牲或延迟部分请求流量以保证系统整体服务可用。

  限流算法

  固定窗口计数

  将时间划分为多个窗口;

  在每个窗口内每有一次请求就将计数器加一;

  如果计数器超过了限制数量,则本窗口内所有的请求都被丢弃,当时间到达下一个窗口时,计数器重置。

  
将时间划分为多个区间;

  在每个区间内每有一次请求就将计数器加一维持一个时间窗口,占据多个区间;

  每经过一个区间的时间,则抛弃最老的一个区间,并纳入最新的一个区间;

  如果当前窗口内区间的请求计数总和超过了限制数量,则本窗口内所有的请求都被丢弃。

  
将每个请求视作"水滴"放入"漏桶"进行存储;

  "漏桶"以固定速率向外"漏"出请求来执行如果"漏桶"空了则停止"漏水";

  如果"漏桶"满了则多余的"水滴"会被直接丢弃。

  
令牌以固定速率生成;

  生成的令牌放入令牌桶中存放,如果令牌桶满了则多余的令牌会直接丢弃,当请求到达时,会尝试从令牌桶中取令牌,取到了令牌的请求可以执行;

  如果桶空了,那么尝试取令牌的请求会被直接丢弃。

  
在实现上是相同的基本算法,描述不同。

  给定等效参数的情况下,这两种算法会将完全相同的数据包视为符合或不符合。

  两者实现的属性和性能差异完全是由于实现的差异造成的,即它们不是源于底层算法的差异。

  
漏桶算法在用作计量时,可以允许具有抖动或突发性的一致输出数据包流,可用于流量管制和整形,并且可以用于可变长度数据包。

  漏桶 - wikipedia

  令牌桶 - wikipedia

  
利用 Spring 拦截器实现

  使用方式:Controller 方法或类加上限流注解,请求到达拦截器时进行拦截处理

  使用 Redis 记录数据,Lua 保证多个命令原子性执行。

  
@RequestMapping("/ratelimit/custom")

  @RateLimit(threshold = 10, rateLimiter = RateLimiterEnum.FIXED_WINDOW, time = 10, timeUnit = TimeUnit.SECONDS)

  public class RateLimitController {

   @GetMapping("/fixed/window")

   @RateLimit(threshold = 10, rateLimiter = RateLimiterEnum.FIXED_WINDOW, time = 10, timeUnit = TimeUnit.SECONDS)

   public ResponseResult String fixedWindow(Long id) {

   id += RandomUtil.randomLong();

   log.info("custom:fixedWindow:{}", id);

   return ResponseResult.success("custom:fixedWindow:" + id);

  

 

 

  


@Override

 

  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

   if (!(handler instanceof HandlerMethod)) {

   return true;

   HandlerMethod handlerMethod = ((HandlerMethod) handler);

   // 从方法和类上获取注解

   RateLimit annotation = AspectUtil.findMethodOrClassAnnotation(handlerMethod.getMethod(),

   RateLimit.class);

   if (annotation == null) {

   return true;

   AspectKeyTypeEnum.KeyTypeData data = AspectKeyTypeEnum.KeyTypeData.builder()

   .prefix("rate:limit").key(annotation.key()).build();

   String limitKey = annotation.keyType()

   .obtainTypeKey(handlerMethod.getMethod(), handlerMethod.getMethodParameters(), data);

   RateLimiterEnum limiterEnum = annotation.rateLimiter();

   // 执行限流脚本

   Long isLimit = redisUtil.execute(limiterEnum.obtainScript(),

   Lists.newArrayList(limitKey), limiterEnum.obtainArgvs(annotation).toArray());

   if (isLimit != null isLimit != 0L) {

   return true;

   throw new ResponseException(ResponseEnum.RATE_LIMITED);

  

 

  
if (count capacity) then

   -- 往 zset 中添加一个值、得分均为当前时间戳的元素,[value,score]

   redis.call("ZADD", key, now, ukid);

   -- 重置 zset 的过期时间,单位毫秒

   redis.call("PEXPIRE", key, ttl);

   res = 1;

   -- 被限流返回0,未被限流返回1

   return res;

  

 

 

  
-- 当前存储的水量,默认为0,存在保存值使用保存值

   local stored_water = tonumber(info[2]);

   if (stored_water == nil) then

   stored_water = 0;

   if (last_time ~= nil) then

   -- 根据上次处理时间和当前时间差,计算流出后的水量

   local leaked_water = math.floor((now - last_time) * rate);

   stored_water = math.max(stored_water - leaked_water, 0);

   if (leaked_water 0) then

   last_time = nil;

   -- 首次访问、泄露了水 设置上次处理时间

   if (last_time == nil) then

   redis.call("HSET", key, "last_time", now);

   -- 被限流返回0,未被限流返回1

   local res = 0;

   if (capacity stored_water) then

   redis.call("HSET", key, "stored_water", stored_water + 1);

   res = 1;

   redis.call("PEXPIRE", key, ttl);

   return res;

  

 

 

  
local last_time = tonumber(info[1]);

   -- 令牌数量,默认为最大访问量,存在保存值使用保存值

   local stored_tokens = tonumber(info[2]);

   if (stored_tokens == nil) then

   stored_tokens = capacity;

   if (last_time ~= nil) then

   -- 根据上次处理时间和当前时间差,触发式往桶里添加令牌

   local add_tokens = math.floor((now - last_time) * rate);

   stored_tokens = math.min(add_tokens + stored_tokens, capacity);

   if (add_tokens 0) then

   last_time = nil;

   -- 首次访问、添加了令牌 设置上次处理时间

   if (last_time == nil) then

   redis.call("HSET", key, "last_time", now);

   -- 被限流返回0,未被限流返回1

   local res = 0;

   if (stored_tokens 0) then

   redis.call("HSET", key, "stored_tokens", stored_tokens - 1);

   res = 1;

   redis.call("PEXPIRE", key, ttl);

   return res;

  

 

 

  以上就是限流(限流保护器)的详细内容,想要了解更多 限流的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: