微服务架构下的分布式限流方案全解析

1.微服务限流

随着微服务的流行,服务和服务之间的稳定性变得愈来愈重要。缓存、降级和限流是保护微服务系统运行稳定性的三大利器。前端

缓存的目的是提高系统访问速度和增大系统能处理的容量,而降级是当服务出问题或者影响到核心流程的性能则须要暂时屏蔽掉,待高峰或者问题解决后再打开,而有些场景并不能用缓存和降级来解决,好比稀缺资源、数据库的写操做、频繁的复杂查询,所以需有一种手段来限制这些场景的请求量,即限流。java

好比当咱们设计了一个函数,准备上线,这时候这个函数会消耗一些资源,处理上限是1秒服务3000个QPS,但若是实际状况遇到高于3000的QPS该如何解决呢?git

因此限流的目的应当是经过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦达到限制速率就能够拒绝服务、等待、降级。github

学习如何去实现一个分布式限流框架,首先,咱们须要去了解最基本的两种限流算法。算法

2.限流算法

2.1漏桶算法

漏桶算法思路很简单,水(也就是请求)先进入到漏桶里,漏桶以必定的速度出水,当水流入速度过大会直接溢出,而后就拒绝请求,能够看出漏桶算法能强行限制数据的传输速率。示意图(来源网络)以下:数据库

2.2令牌桶算法

令牌桶算法和漏桶算法效果同样但方向相反的算法,更加容易理解。随着时间流逝,系统会按恒定1/QPS时间间隔(若是QPS=100,则间隔是10ms)往桶里加入令牌(想象和漏洞漏水相反,有个水龙头在不断的加水),若是桶已经满了就再也不加了。新请求来临时,会各自拿走一个令牌,若是没有令牌可拿了就阻塞或者拒绝服务。示意图(来源网络)以下:数组

2.3算法选择

漏桶算法与令牌桶算法的区别在于,漏桶算法可以强行限制数据的传输速率,令牌桶算法可以在限制数据的平均传输速率的同时还容许某种程度的突发状况。令牌桶还有一个好处是能够方便的改变速度。一旦须要提升速率,则按需提升放入桶中的令牌的速率。因此,限流框架的核心算法仍是以令牌桶算法为主。缓存

3.本地限流

已知上面讲述的令牌桶算法的原理,如何经过代码实现?服务器

本地限流的实现能够用Long长整型做为令牌桶,为了达到无锁,建议使用Long的原子类型AtomicLong,使用AtomicLong的好处就是能够很是方便的对其进行CAS加操做与CAS减操做(也就是令牌桶令牌的放入与拿取),以免线程的上下文切换的开销,核心CAS算法以下:网络

private boolean tryAcquireFailed() {
   long l = bucket.longValue();
   while (l > 0) {
      if (bucket.compareAndSet(l, l - 1)) {
          return true;
      }
      l = bucket.longValue();
   }
   return false;
}
复制代码

根据上述了解的令牌桶算法能够得知,令牌桶须要一个ScheduledThread不断的放入令牌,这部分的代码以下:

ScheduledThreadExecutor.scheduleAtFixedRate(() -> 
    bucket.set(rule.getLimit()), rule.getInitialDelay(), rule.getPeriod(), rule.getUnit()
);
复制代码

4.分布式限流概述

分布式限流须要解决什么问题呢?我想至少有下面几个:

1.动态规则: 好比限流的QPS咱们但愿能够动态修改,限流的功能能够随时开启、关闭,限流的规则能够跟随业务进行动态变动等。

2.集群限流: 好比对Spring Cloud微服务架构中的某服务下的全部实例进行统一限流,以控制后续访问数据库的流量。

3.熔断降级: 好比在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而致使级联错误。

可选的其它几个功能,诸如实时监控数据、网关流控、热点参数限流、系统自适应限流、黑白名单控制、注解支持等,这些功能其实能够很是方便的进行扩展。

5.分布式限流方案

分布式限流的思想我列举下面三个方案:

1.Redis令牌桶

这种方案是最简单的一种集群限流思想。在本地限流中,咱们使用Long的原子类做令牌桶,当实例数量超过1,咱们就考虑将Redis用做公共内存区域,进行读写。涉及到的并发控制,也可使用Redis实现分布式锁。

方案的缺点显而易见,每取一次令牌都会进行一次网络开销,而网络开销起码是毫秒级,因此这种方案支持的并发量是很是有限的。

2.QPS统一分配

这种方案的思想是将集群限流最大程度的本地化。

举个例子,咱们有两台服务器实例,对应的是同一个应用程序(Application.name相同),程序中设置的QPS为100,将应用程序与同一个控制台程序进行链接,控制台端依据应用的实例数量将QPS进行均分,动态设置每一个实例的QPS为50,如果遇到两个服务器的配置并不相同,在负载均衡层的就已经根据服务器的优劣对流量进行分配,例如一台分配70%流量,另外一台分配30%的流量。面对这种状况,控制台也能够对其实行加权分配QPS的策略。

客观来讲,这是一种集群限流的实现方案,但依旧存在不小的问题。该模式的分配比例是创建在大数据流量下的趋势进行分配,实际状况中可能并非严格的五五分或三七分,偏差不可控,极容易出现用户连续访问某一台服务器遇到请求驳回而另外一台服务器此刻空闲流量充足的尴尬状况。

3.发票服务器

这种方案的思想是创建在Redis令牌桶方案的基础之上的。如何解决每次取令牌都伴随一次网络开销,该方案的解决方法是创建一层控制端,利用该控制端与Redis令牌桶进行交互,只有当客户端的剩余令牌数不足时,客户端才向该控制层取令牌而且每次取一批。

这种思想相似于Java集合框架的数组扩容,设置一个阈值,只有当超过该临界值时,才会触发异步调用。其他存取令牌的操做与本地限流无二。虽然该方案依旧存在偏差,但偏差最大也就一批次令牌数而已。

6.开源项目

上面说了三种分布式限流方案的实现思路,这里推荐一个基于发票服务器思想实现的分布式限流项目SnowJean

笔者经过该项目源码观察到该限流项目在解决分布式限流上的有不少巧妙的点,好比,SnowJean内部使用观察者模式实现动态规则配置,使用工厂模式实现限流器的构造,使用建造者模式构建限流规则。

在解决如何对客户端实例的健康情况进行检查时,利用的是Redis的过时时间与客户端发送的心跳包(发送心跳时再进行延期)。比较不错的一点是,该项目提供基于前端Echarts图表的QPS视图展现,以下图。

相关文章
相关标签/搜索