探索Java分布式限流技术
作者:桃花猿
一、概述
1.1 主要解决的问题
- 访问请求流量远远大于服务器的负载,致使服务器宕机,导致整个服务的不可用;-
限流
- 当前服务调用其他服务,其他服务不可用,导致当前服务的调用一直超时,进而当前服务的线程资源耗尽,进而服务宕机,产生服务雪崩。-
服务熔断【或降级】
1.2 限流定义
通过限制并发数或一段时间窗口允许处理的最大连接数
来保护系统,一旦超过设定阈值,对当前请求进行处理。主要的处理方式有:跳转到错误页面、进入排队系统或降级
等。本质上,就是损失一部分用户的可用性,来保证整体的可用性
。
1.3 常见限流规则
1. QPS或连接数控制;例如对单个IP的限制、单台机器的限制、单个服务组的限制或整个机房的限制 2. 传输速率:例如资源的下载速度 3. 黑白名单。
1.4 分布式限流
把整个分布式环境中所有服务器当做一个整体来考量。比如说针对IP的限流,我们限制了1个IP每秒最多10个访问,不管来自这个IP的请求落在了哪台机器上,只要是访问了集群中的服务节点,那么都会受到限流规则的制约。必须将限流信息保存在一个中心化的组件上,这样它就可以获取到集群中所有机器的访问状态,目前有两个比较主流的限流方案:
网关层限流
:应用在流量入口处;中间件限流
:将限流信息存储在分布式环境中某个中间件里(比如Redis缓存),每个组件都可以从这里获取到当前时刻的流量统计,从而决定是拒绝服务还是放行流量。
二、常用的限流方案
2.1 Guava限流
一种Google研发的客户端限流组件,非常轻量级。但仅支持单机限流
。底层是令牌桶限流算法
。
2.2 网关层限流
将系统流量的分布层次抽象成一个简单的漏斗模型来看。
上面是一个最普通的流量模型,从上到下的路径依次是:
用户流量从网关层转发到后台服务后台服务承接流量,调用缓存获取数据缓存中无数据,则访问数据库
Nginx限流
:
基于IP地址和基于服务器的访问请求限流并发量(连接数)限流下行带宽速率限制
SpringCloud 的GateWay组件
限流。
2.3 中间件限流
方案1: Redis+LUA脚本实现限流:
方案2: Sentinal限流:底层使用滑动窗口算法来限流。
三、常用限流算法
3.1 计数器限流
概述:
在一定周期内累加访问次数,当访问次数达到阈值时,触发限流策略,当进入下一个时间周期时,计算器会清零,重新计数。适用场景:
短信发送次数限制存在问题:
临界问题,会造成系统的抖动。假设时间周期为1s,阈值为50。开始时间是32s, 32.0-32.8s请求次数为10,32.8-33s请求次数为30,33.0-33.2请求次数为30,33.3-34s请求次数为30,可以看出在32s和33s时间窗口内都没有触发限流阈值,但在32.8-33.2区间内超过限流阈值,造成系统的不稳定。
3.2 滑动窗口限流
概述:
解决了计数器限流算法的临界问题。将固定周期划分为n个小的时间段,分别在每个时间段内统计请求访问次数,然后根据时间将窗口向前滑动并删除过期的时间段,时间段越短统计的越准,很好的解决了临界问题适用场景:
sentinel底层采用该算法存在问题:
窗口大小选择以及n大小的选择。
3.3 令牌桶限流
概述:
对于每一个请求,都需要先从令牌桶中获取令牌。若获取不到令牌,触发限流策略。系统会以一个恒定速率
向令牌童中放入令牌,令牌中满了后直接丢弃令牌适用场景:
网络流量整形和速率限制,适合处理突发流量
。当突发流量来的时候,可以直接获取令牌中所有的令牌进行快速处理。Guava底层就采用该算法存在问题:
虽然可以处理突发流量,但后台系统的压力也会瞬间增大,要合理设置令牌数量。
3.4 漏桶限流算法
概述
:控制数据注入网络的速度。在漏桶内部维护一个容器,这个容器会以恒定速率出水,不论漏桶的流入速度多快,出水的速度是固定
的,消息中间件就利用了该思想。适用场景
:流量平滑存在的问题
:无法处理突发流量。漏桶的天然特性决定了它不会发生突发流量,就算每秒1000个请求到来,那么它对后台服务输出的访问速率永远恒定。而令牌桶则不同,其特性可以预存
一定量的令牌,因此在应对突发流量的时候可以在短时间消耗所有令牌,其突发流量处理效率会比漏桶高,但是导向后台系统的压力也会相应增多。
四、Guava流量预热
4.1 概述
核心思想:
动态的调整令牌的发放速度,让流量变化更加平滑。例如:某个接口设定了100个Request每秒的限流标准,同时使用令牌桶算法做限流。假如当前时间窗口内都没有Request过来,那么令牌桶中会装满100个令牌。如果在下一秒突然涌入100个请求,这些请求会迅速消耗令牌,对服务的瞬时冲击会比较大。
4.2 预热模型
其中横坐标是令牌桶的当前容量,纵坐标是令牌发放速率。
闲时到忙时的流量转变:
假定当前我们处于闲时流量阶段,没几个访问请求,这时令牌桶是满的。接着在下一秒突然涌入了10个请求,这些请求开始消耗令牌桶中的令牌。在初始阶段,令牌的放行速度比较慢,在第一个令牌被消耗以后,后面的请求要经过3x时间间隔也就是0.3s才会获取第二块令牌。随着令牌桶中令牌数量被逐渐消耗,当令牌存量下降到最大容量一半的时候(Half位置),令牌放行的速率也会提升,以稳定间隔0.1s发放令牌。流量从忙时转变为闲时:
令牌发放速率是由快到慢逐渐变化。起始阶段的令牌放行间隔是0.1s,随着令牌桶内令牌逐渐增多,当令牌的存量积累到最大容量的一半后,放行令牌的时间间隔进一步增大为0.3s。
RateLimiter正是通过这种方式来控制令牌发放的时间间隔,从而使流量的变化更加平滑。
五、Sentinel工作原理
概述:
一款高性能的分布式限流组件,能够保护微服务架构下的应用程序,实现服务限流和服务降级熔断等。主要原理
:利用AOP切面
来拦截所有的请求,对请求进行实时计数、实时统计等,并根据设定的阈值来决定是否放行该请求。主要流程
为:
1. 规则配置。利用sentinel提供的功能来配置限流规则,即每分钟请求的次数。限流规则会被加载到内存中,sentinel会利用这些限流规则进行限流 2. 请求拦截。在程序处理处理之前,sentinel利用AOP切面拦截所有的请求; 3. 统计请求。拦截后,sentinel会对请求进行实时统计,在内存中维护的时间窗口内统计请求qps,响应时间以及异常次数等; 4. 判断流量。如果qps超过设定的阈值,则进行限流处理,包括降级等;若调用其他服务的响应时间或异常次数超过阈值,则直接熔断服务,不再调用其他服务,返回一个默认的降级值 5. 动态调整限流。可以在运行期间,sentinel可以修改阈值,删除规则等
到此这篇关于探索Java分布式限流技术的文章就介绍到这了,更多相关Java分布式限流内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!