目录
一、高并发系统三大利器
保护高并发系统的三大利器:限流、熔断降级、缓存。
限流是指在面临瞬时巨大流量访问系统时(商品秒杀等)为了保证系统的可用性的一个限制手段。
熔断降级一般一起使用,是为了在某些大流量业务场景(双11双12等)下保证核心业务可用的解决方案。(降级是主动的,熔断是被动的。熔断是指当上游服务调用下游服务出现不可用时,暂时切断请求,防止系统雪崩;降级是指某些情况下保证核心业务,将边缘业务服务暂时关闭。)
缓存是为了缓解数据库的查询压力,对某些热点数据和核心业务数据添加缓存层进行访问,高并发系统常使用Redis作为缓存层。(做数据冗余,以空间换时间)
对于一个微服务架构,要考虑的是服务内调用和对外提供服务两个方面。
服务内调用可以使用线程池进行资源隔离,对调用方进行限流,服务降级等方案保证服务的可用。(例如:Hystrix熔断器不仅可以满足熔断降级,也可以设置调用时的线程数作为资源隔离和服务内限流的解决方案)
对外提供服务可以使用应用网关或者全局网关对系统整体进行限流。(例如:SpringGateway作应用网关通过Filter过滤器做一些限流操作;Nginx作为全局网关和反向代理服务器对应用网关进行负载均衡和限流)
二、限流算法
对于微服务架构,整体的限流要安排在全局网关,这里全局网关可以使用Sentinel、Nginx、Kong等。其底层实现实际是基于限流算法。
2.1 固定窗口算法(Fixed Window)
固定窗口算法也称为计数器算法,就是通过维护一个单位时间内(比如每秒)的计数值,每当一个请求通过时,就将计数值加1,当计数值超过预先设定的阈值时,就拒绝单位时间内的其他请求。如果单位时间已经结束,则将计数器清零,开启下一轮的计数。
比如每秒限制100个请求,Java代码如下:
// 固定窗口算法Java代码实现
public class FixedWindow {
private long time = new Date().getTime();
private Integer count = 0; // 计数器
private final Integer max = 100; // 请求阈值
private final Integer interval = 1000; // 窗口大小(固定窗格)
public boolean trafficMonitoring() {
long nowTime = new Date().getTime();
if (nowTime < time + interval) {
// 在时间窗口内
count++;
return max > count;
} else {
time = nowTime; // 开启新的窗口
count = 1; // 初始化计数器,这个请求属于当前新开的窗口
return true;
}
}
}
但这里出现了一个问题,我们设定1秒内允许通过的请求阈值是100,如果有用户在时间窗口的最后几毫秒发送了100个请求,紧接着又在下一个时间窗口开始时发送了100个请 求,那么这个用户其实在一秒内成功请求了200次,显然超过了阈值但并不会被限流,其实这就是流量突刺问题,那么临界值问题要怎么解决呢?
2.2 滑动窗口算法(Sliding Window)
计数器滑动窗口法就是为了解决上述固定窗口计数存在的问题而诞生,滑动窗口是基于时间来划分窗口的。(相当于对固定窗口进行了划分)
前面说了固定窗口存在临界值问题,要解决这种临界值问题,显然只用一个窗口是解决不了问题的。假设我们仍然设定1秒内允许通过的请求是200个,但是在 这里我们需要把1秒的时间分成多格,假设分成5格(格数越多,流量过渡越平滑),每格窗口的时间大小是200毫秒