一个业务系统在指定配置的服务器上,可以承载的容量是一定的,当请求流量超过系统的容量后,系统就会变得不稳定,可用性下降,为了保证系统的可用性,需要将系统能够承载容量之外的流量进行丢弃,这样虽然会导致部分用户请求失败,但是整个系统依然是可用的,依然能对外提供服务,而不是因为负载压力太大导致整个系统崩溃,使所有用户都不能访问,这就是保证系统高可用的限流方案。

目前业内实现限流的算法有四种,分别是固定窗口算法,滑动窗口算法,漏桶算法和令牌桶算法。

为方面下文代码实现,我们定义了接口和对应的抽闲类:
1.接口定义:

public interface LimitAlgo {// 获取限流资源boolean tryAcquire();
}

2.抽象类定义

public abstract class AbstractLimitAlgo implements LimitAlgo{// 资源限制protected int limit;// 最近获取限流资源时间戳protected long lastTimeStamp;public AbstractLimitAlgo(int limit){this.limit = limit;this.lastTimeStamp = System.currentTimeMillis();}protected long getElapseTimeFromLast() {return System.currentTimeMillis() - lastTimeStamp;}
}

固定窗口算法

固定窗口算法是一个相对比较简单的限流算法,该算法是将一个固定的时间单元当做一个时间窗口,每个窗口仅允许限制的流量内的请求通过,具体如下图:

我们将是事件线切分成一个一个的限流窗口,每个限流窗口有一个窗口的开始和结束时间,窗口开始时,计数器清零,在这个窗口的时间范围内,每进来一个请求,计数器+1,如果计数器记录值超过限流阈值,就拒绝服务,直接给客户端响应503。当限流窗口结束后,进入下一个限流窗口,计数器再次清零,重新开始计数。
固定窗口的限流算法的核心代码实现如下:

public class FixedWindow extends AbstractLimitAlgo {private long windowLength;private AtomicInteger counter = new AtomicInteger(0);private Lock lock = new ReentrantLock();public FixedWindow(int limit,long windowLength) {super(limit);this.windowLength = windowLength;}@Overridepublic boolean tryAcquire() {long elapsedTimeStamp = getElapseTimeFromLast();if(elapsedTimeStamp >= windowLength) {lock.lock();try{if(getElapseTimeFromLast() >= windowLength) {lastTimeStamp = System.currentTimeMillis();counter.getAndSet(0);}}catch (Exception e) {e.printStackTrace();}finally {lock.unlock();}}return counter.incrementAndGet() <= limit;}
}

滑动窗口算法实思想比较简单,但是限流效果比较差,会出现 2倍配置速率问题,导致无法达到限流的目的。简单来说,当限流算法进入一个新的窗口后,计数器会重新计数,我们假设每个窗口限流100个请求,在第一个限流窗口快要结束时,突然进入进入100个请求,因为这个请求量在限流范围内,所以没有触发限流,请求全部通过,然后进入第二个限流窗口,计数器重新计数,这时又忽然进来100个请求,因为此时在第二个限流窗口,所以也没有触发限流,这就导致在短时间内进入了200个请求,这样会给系统造成巨大的负载压力。具体示意图如下:

滑动窗口

为了解决固定窗口2倍速率配置的问题,我们可以采用滑动窗口限流。滑动窗口之所以可以解决固定窗口2倍速率配置的问题,是因为滑动窗口将限流窗口划分成多个更细粒度的分片单元,滑动窗口每次只滑动一个分片单元,短时间出现的请求仍然在一个滑动窗口内,仍然可以被滑动窗口限流,也就是说,前一个滑动窗口的请求数量可以对下一个滑动窗口产生影响。而固定窗口的两个窗口之间完全没有任何关系,这也是固定窗口产生2倍速率配置问题的根本原因。滑动窗口示意图如下:

滑动窗口的实现方式和固定窗口基本是一致的,只不过要改动重置“窗口计数器”和“当前窗口结束时间”的逻辑就可以了,在重置窗口结束时间方面:固定窗口算法将窗口结束时间置为+1 窗口长度,而滑动窗口算法将窗口结束时间为+1时间片长度。在重置计数器方面:固定窗口算法直接将计数器置为0,而滑动窗口则是将计数器置为窗口包含时间片中所有请求之和。

滑动窗口实现代码如下:

public class SlideWindow extends AbstractLimitAlgo {private long windowLength;private AtomicInteger[] windowPlice;private AtomicInteger counter = new AtomicInteger(0);private Lock lock =  new ReentrantLock();private volatile int index;private int newCounter;public SlideWindow(int limit, long windowLength, int unitCnt) {super(limit);this.windowLength = windowLength;this.lastTimeStamp = System.currentTimeMillis();this.windowPlice = new AtomicInteger[unitCnt];for(int i=0;i<unitCnt;i++) {windowPlice[i] = new AtomicInteger(0);}}@Overridepublic boolean tryAcquire() {long unitLength = windowLength / windowPlice.length;long elapseTimeStamp = getElapseTimeFromLast();if(elapseTimeStamp >= unitLength) {lock.lock();try{if(getElapseTimeFromLast() >= unitLength){newCounter += windowPlice[index].intValue();index = ++ index % windowPlice.length;newCounter -= windowPlice[index].intValue();windowPlice[index].getAndSet(0);counter.getAndSet(newCounter);lastTimeStamp = System.currentTimeMillis();}}catch (Exception e) {e.printStackTrace();}finally {lock.unlock();}}boolean result = counter.incrementAndGet() <= limit;if(result) {windowPlice[index].incrementAndGet();}return result;}
}

总结

本文介绍了限流算法中的固定窗口算法和滑动窗口算法,固定窗口算法实现简单,但是会出现2倍配置速率问题,滑动窗口通过将窗口划分成多个细粒度的时间分片,每次移动一个小的时间分片,来解决固定窗口的中的限流失效的问题。对于漏桶算法和令牌桶 算法,放在下一篇文章中讨论。

微服务中常用的限流算法(一)相关推荐

  1. Java中常用的限流算法

    在Java的系统中,在一些活动日或者是被黑客攻击,导致访问量突然暴增,系统承受不了巨大的流量冲击而崩溃.为了保护我们的系统,在实际开发中有四种常见的限流算法来保证系统的安全性. 1 固定窗口算法 固定 ...

  2. golang bufio.newscanner如何超时跳出_Golang微服务的熔断与限流

    (给Go开发大全加星标) 来源:Che Dan https://medium.com/@dche423/micro-in-action-7-cn-ce75d5847ef4 [导读]熔断和限流机制对于大 ...

  3. 亿级流量治理系列:常用的限流算法有哪些?

    前言 上篇文章<为什么大公司都要做流量治理?>跟大家聊了下做流量治理的真正目的是什么.如果你要开发一个流量治理的平台或者一个限流的框架,那么必不可少的就是要选择一种合适的限流算法.本篇文章 ...

  4. 服务高可用利器——限流算法介绍与示例

    文章目录 0.前言 1.计数器 1.1 简介 1.2 示例 2.滑动窗口 2.1 简介 2.2 示例 3.漏桶 3.1 简介 3.2 示例 4.令牌桶 4.1 简介 4.2 示例 5.小结 参考文献 ...

  5. 微服务之熔断、限流、降级 三板斧

    系列服务器开发 文章目录 系列服务器开发 前言 一.背景 二.熔断 三. 限流 四. 降级 五.三种措施的差异 总结 前言 Spring Cloud全家桶是提供的一整套微服务开源解决方案,包括服务注册 ...

  6. 微服务架构 - Gateway网关限流

    作者:pu20065226 cnblogs.com/pu20065226/p/11426279.html 1.算法 在高并发的应用中,限流是一个绕不开的话题.限流可以保障我们的 API 服务对所有用户 ...

  7. java中常见的限流算法详细解析

    目录 前言 1. 验证限流以及容器限流 2. 服务端限流 2.1 固定时间窗口 2.2 滑动时间窗口 2.3 漏桶算法 2.4 令牌桶算法 前言 以下的文章参考了一些具体的资料加深了解 B站:Java ...

  8. 深度好文 — 微服务和API网关限流熔断实现关键逻辑思路

    来源:https://www.toutiao.com/i6853970319745483275/?group_id=6853970319745483275 今天准备谈下微服务架构和API网关中的限流熔 ...

  9. SpringCloud微服务组件:Sentinel限流熔断

    点击关注公众号,实用技术文章及时了解 前言 什么是雪崩问题? 微服务之间相互调用,因为调用链中的一个服务故障,引起整个链路都无法访问的情况. 解决雪崩问题的常见方式有四种: 超时处理:设定超时时间,请 ...

最新文章

  1. NeurIPS 2019最佳论文出炉,今年增设“新方向奖”,微软华人学者获经典论文奖...
  2. elasticsearch的多索引联合查询以及范围日期查询示例
  3. conv--向量的卷积和多项式乘法
  4. 如何挂载阿里云Linux服务器的“数据盘”(新购买)
  5. (原创)c#学习笔记08--面向对象编程简介02--OOP技术05--运算符重载
  6. android清理缓存功能吗,Android清理缓存功能实现
  7. 关于TTThumbsViewController加载更多
  8. 如何在 Ubuntu 中安装 QGit 客户端
  9. elasticsearch设置_search的size
  10. network 网络带宽
  11. 开运算和闭运算的性质
  12. 秒杀各大网盘的不限速大文件传输工具
  13. 表达式类型错误oracle,PL/SQL编译错误 - PLS-00382:表达式类型错误
  14. JavaWeb调用顺序
  15. 使用计算机教学的好处,谈计算机在教学中的作用
  16. 纹理的应用(凹凸贴图与法线贴图,三维噪声和三维纹理)
  17. 淘宝电商为什么转型社群团购,你知道吗?
  18. 一个只完成了一部分的小游戏。。。
  19. duet连win10_在Windows PC上使用Duet Display时连接不上Apple设备的解决方法之一
  20. java乱码base64_JavaScript BASE64算法实现(完美解决中文乱码)

热门文章

  1. 108-Spring的底层原理(下篇)
  2. 安装 magenta 失败:rtmidi
  3. Magenta魔改记-0:Magetna初见
  4. 2021-2027全球与中国面部脂肪注射市场现状及未来发展趋势
  5. 【ESP32】问题汇总 更新中
  6. 家乡的春节html,家乡的春节(Spring Festival in my hometown)英语作文
  7. 使用开源安装包制作工具Inno Setup制作软件安装包
  8. 单纯形法Python实现
  9. android webview 图片缓存,WebView 图片离线缓存(含图片)
  10. GAN系列之动漫风格迁移AnimeGAN2