限流 返回首页

发表于 2019-10-29 | 本文共 475 字

一、限流算法

常见的限流算法有:计数器、漏桶、令牌桶。

1.1 计数器

counter

计数器限流,指固定的时间窗口内可以通过的一定数量的请求,超过这个数量后,接下来的请求会被拒绝。比如1s内可以通过60个请求,超过60个后,新来的请求会被拒绝。

计数器算法可以“简单粗暴”的实现限流,但是存在“突刺现象”,如上图中最后1秒所示,60个请求集中在1s的前一小部分,导致剩余的大部分时间的请求都被拒绝。

1.2 漏桶

leaky-bucket

如上图,水进入漏桶里,然后按固定速率流出。因为水流出的速率是固定的,所以当请求过多,装满漏桶后,会出现溢出。

漏桶算法是:缓存请求、匀速处理、多余的请求丢弃。

1.3 令牌桶

token-bucket

令牌桶算法按速率往桶里放token,如果桶里token已满,就不再增加;放token操作和取token操作互不影响。

请求到来时尝试从桶中取token,如果取到token,请求放行;如果没有取到token,请求被拒绝或者在队列中被缓存。

与漏桶算法相比,令牌桶算法在突发流量到来时,可以同时取走多个token,有token就可以处理请求。如果突发流量的时间比较短,token没有被取光,整体上系统不会有影响,所以令牌桶算法在处理突发流量方面要比漏桶好一些。

二、Sentinel

Sentinel提供了流量控制和熔断降级等功能,这些由一系列功能插槽实现。

2.1 Sentinel的插槽时序

sentinel

sentinel的插槽工作时序如上图(以sentinel与zuul集成为例),通过CommonFilter,按插槽的构建顺序调用每个插槽的entryexit方法:

2.2 Sentinel的流控

Sentinel的流控在FlowSlot#entry中处理,流控规则分为QPS和线程数,是当QPS或者线程数超过某个阈值,采取措施进行流量控制。

2.2.1 QPS限流

流控规则是QPS时,监控指标是node的pass count,即一秒内到来到的请求,当pass count超过规则阈值时,对流量进行控制。

2.2.2 线程数限流

流控规则是线程数时,监控指标是node的thread num,即当前处理该资源的线程数,如果thread num超过规则阈值时,对流量进行控制。

2.3 Sentinel的降级

Sentinel的降级在DegradeSlot#entry中处理,降级规则分为rt(response time)和异常两种类型,异常又细化为异常比例和异常数。

2.3.1 rt(response time)降级

计算node的平均响应时间(avgRt),avgRt = rt / success count,当1s内持续进入5个请求,对应时刻的avgRt均超过阈值,那么在接下的时间窗口之内,对这个方法的调用都会自动地熔断。

2.3.2 异常降级

异常降级分为异常数和异常比例两种,异常数降级是node的exception > 设置的值时,请求被降级;异常比例降级是node的exception / success > 设置的值每秒请求量≥5时,请求被降级。

异常降级针对业务异常,不包括BlockException

三、Hystrix

Hystrix提供了信号量隔离、线程池隔离、熔断和降级,来构建稳定、可靠的分布式系统。

3.1 信号量隔离

信号量的实现是一个简单的计数器,是TryableSemaphore接口的具体实现TryableSemaphoreActual,当请求进入熔断器时,执行TryableSemaphore#tryAcquire,计数器加1,结果大于阈值的话,就返回false,发生信号量拒绝事件,执行降级逻辑。当请求离开熔断器时,执行TryableSemaphore#release,计数器减1。

3.2 线程池隔离

最终,在调用下游微服务时,是由另外的线程hystrix-RibbonCommand-x来完成的(AbstractCommand#executeCommandWithSpecifiedIsolation),由此来实现线程分离,从而达到资源隔离的作用。当线程池来不及处理并且请求队列塞满时,新进来的请求将快速失败,可以避免依赖问题扩散。

线程池和信号量都支持熔断和限流。相比线程池,信号量不需要线程切换,因此避免了不必要的开销。但是信号量不支持异步,也不支持超时,也就是说当所请求的服务不可用时,信号量会控制超过限制的请求立即返回,但是已经持有信号量的线程只能等待服务响应或从超时中返回,即可能出现长时间等待。线程池模式下,当超过指定时间未响应的服务,Hystrix会通过响应中断的方式通知线程立即结束并返回。

3.3 熔断

hystrix-circuit-breaker

HystrixCircuitBreaker接口有3个方法allowRequestisOpenmarkSuccess,分别用来判断请求是否可以通行、断路器是否打开和闭合断路器。 HystrixCircuitBreakerImpl是熔断器的实现,定义了allowSingleTest方法用来检测服务是否恢复。

熔断器的几个方法如下:

总结,请求到来时:

3.4 降级

Hystrix在以下几种情况下会走降级逻辑,保护当前服务不受依赖服务的影响:

四、Hystrix的不足

  1. Hystrix的线程池隔离因为开销大,不适合网关这种代理几十至上百个微服务的应用。如果要与网关配合使用的话,需要使用信号量隔离。
  2. Hystrix的信号量隔离是服务级别的,Sentinel类似的线程数限流可以做到接口级别。
  3. Hystrix的信号量隔离目前不支持任务排队和主动超时。不支持请求排队,如果获取信号量失败,就会直接降级。获取信号量成功的请求因为只能等待服务响应或从超时中返回,所以可能出现长时间等待。
  4. Hystrix的作者同时也是RxJava的作者,因此Hystrix使用了大量的RxJava,对于不熟悉RxJava的人,扩展有一定门槛。
  5. Hystrix稳定但是现在已处于维护模式,除了修bug,不会有新的功能加入。
  6. 功能较Sentinel少,Sentinel有黑/名单和系统保护等功能。
  7. Hystrix缺少简便的配置项实时刷新功能。

与Sentinel相比,Hystrix的优势:

  1. Hystrix以jar包形式与Spring Cloud全家桶集成,使用轻便。Sentinel如果要发挥最大作用的话,需要配合控制台、进行规则持久化、使用集群限流依赖token server。
  2. Hystrix稳定,稳定到Netflix认为已经可以满足他们的需求,现在已经进入维护模式。

Hystrix (at version 1.5.18) is stable enough to meet the needs of Netflix for our existing applications。

参考:
Sentinel介绍
Hystrix