文章目录
- 1、Sentinel与Hystrix的区别
- 2、限流算法
- 3、限流算法对比
- 4、Sentinel限流与Gateway限流
1、Sentinel与Hystrix的区别
线程隔离有两种方式实现:
- 线程池隔离(Hystrix默认采用)
- 信号量隔离(Sentinel默认采用)
服务I需要远程调用服务A、服务B,则创建两个线程池,分别用来处理服务I–>服务A,和服务I–>服务B的请求。和线程池隔离不同的是,信号量隔离比较轻量级,就维护一个计数器就好,不用去维护线程数量。二者的优缺点来说,线程池隔离的优点是:
- 支持主动超时:线程是我定义的,我自己可以来中断、超时
- 支持异步调用
缺点则是:
- 线程的额外开销比较大
总结就是:
Hystix默认是基于线程池实现的线程隔离,每一个被隔离的业务都要创建一个独立的线程池,线程过多会带来额外的CPU开销,性能一般,但是隔离性更强
。
Sentinel是基于信号量(计数器)实现的线程隔离,不用创建线程池,性能较好,但是隔离性一般
。
2、限流算法
限流:对应用服务器的请求做限制,避免因过多请求而导致服务器过载甚至宕机。
限流算法常见的包括两种:
- 计数器算法,又包括窗口计数器算法、滑动窗口计数器算法
- 令牌桶算法(Token Bucket)
- 漏桶算法(Leaky Bucket)
计数器算法之固定窗口计数器算法:
固定窗口计数器的实现为:
- 将时间划分为多个窗口,窗口的时间跨度称为Interval,比如取1000ms
- 每个窗口维护一个计数器,每有一次请求就将计数器加一,限流就是设置计数器阈值,本例为3
- 当该窗口所在时间里,请求数超过阈值,则超出部分的请求直接都被丢弃
固定窗口计数,有个问题就是:如下图,当4000-5000ms的前500ms没请求,4500-5000ms刚好三个请求,此时,未达阈值。5000-6000ms中,前500ms有三个请求,后500ms没请求,则5000-6000也未达阈值。但从4500-5500ms这一秒,则有6个请求,如果服务真的最多只能承受QPS3,则这样服务可能宕机。
计数器算法之滑动窗口计数器算法:
滑动窗口计数器算法会将一个窗口划分为n个更小的区间:(在窗口的基础上引入区间的概念)
- 窗口时间跨度Interval为1秒;区间数量 n = 2 ,则每个小区间时间跨度为500ms
- 限流阈值依然为3,时间窗口(1秒)内请求超过阈值时,超出的请求被限流
- 此时的窗口不再是固定的1000-2000ms或者1500-2500ms,而是 根据当前请求所在时间(currentTime)移动 ,
这个滑动窗口范围是从(currentTime减去Interval)之后的第一个时区开始,到currentTime所在时区结束,来做为统计的窗口
举个例子:如上图,比如1250ms有个请求进来,则1250-1000=250,250ms之后的第一个时区则是500到1000区间,1250ms自身所在时区为1000到1500ms,所以对应的滑动窗口就是500到1500ms这个时区。(图中紫色虚线)
如上图,再往后,1300ms有个请求,此时,滑动窗口计算后还是500-1500ms,这个窗口当前共有三个请求,分别在900ms、1250ms、1300ms到达。此时1400ms再来一个,滑动窗口计算后还是500-1500ms,但显然这个窗口QPS已经到3了,1400ms的请求会被丢弃。到此,固定窗口的统计问题得到了解决。
再往后,2100ms,则计算后,滑动窗口在1500到2500ms(紫色虚线),未达QPS阈值,但看1250ms到2100ms这850ms的时间,不到一个窗口时间跨度Interval,却有4个请求 > 3 。这个就是区间划分的问题了,区间越小,则限流越准。 比如以125ms为一个区间,2100-1000=1100,在1000到1125区间,下一个为1125到1250,所以滑动窗口为1125到2125ms,这个窗口已有1250、1300、1600到的三个请求,因此2100会被丢弃。
当然,不管这个区间多小,总有可能有卡边界前后请求导致QPS超过的情况。考虑上现实世界,这个窗口是没必要设置成极限值的,比如1ms,差不多100ms就已经可以解决大多情况。
令牌桶算法:
- 以固定的速率生成令牌,存入令牌桶中,如果令牌桶满了以后,多余令牌丢弃
- 请求进入后,必须先尝试从桶中获取令牌,获取到令牌后才可以被处理
- 如果令牌桶中没有令牌,则请求等待或丢弃
漏桶算法:
- 将每个请求视作"水滴"放入"漏桶"进行存储
- "漏桶"以固定速率向外"漏"出请求来执行,如果"漏桶"空了则停止"漏水”
- 如果"漏桶"满了则多余的"水滴"会被直接丢弃
- 可以理解成请求在桶内排队等待
Sentinel在实现漏桶算法时,采用了排队等待模式。让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。并发的多个请求必须等待,预期的等待时长 =最近一次请求的预期等待时间 + 允许的间隔。如果请求预期的等待时间超出最大时长,则会被拒绝。
例如:
- QPS = 5,意味着每200ms处理一个队列中的请求;
- timeout = 2000,意味着预期等待超过2000ms的请求会被拒绝并抛出异常
3、限流算法对比
因为计数器算法一般都会采用滑动窗口计数器,所以这里我们对比三种算法:
4、Sentinel限流与Gateway限流
限流算法常见的有三种实现:滑动时间窗口、令牌桶算法、漏桶算法。Gateway则采用了基于Redis实现的令牌桶算法。而Sentinel内部却比较复杂:
- 默认限流模式是基于滑动时间窗口算法
- 排队等待的限流模式则基于漏桶算法
- 而热点参数限流则是基于令牌桶算法