过载保护初探
过载原理
过载是指当前负载超出了系统的最大处理能力,例如系统的极限QPS是100,但实际的请求达到了1000QPS
对一个完全没有过载保护的系统来说,原本是0.01秒完成的请求,并不是直觉上皆大欢喜的0.1秒完成
而是一部分请求稍微延长了请求时间,而另外一部分请求超时很多秒
这种现象的本质原因,是因为不管是基于epoll的异步调度模型,还是golang的协程模型等等
网络,CPU,内存,磁盘不可能完全的分配均衡
一个请求在到达服务游时,就可能会因为这种不均衡的调度产生饥饿现象,等待很久。
更糟糕的是因此导致的雪崩现象
大量处理无用功(单个后台服务雪崩)
饥饿的请求也会去被处理,但是处理的时候已经超时很久了,客户端可能已经断开了请求(实际的客户因为不耐烦或是上游超时断开),此时请求即使处理了也是无用功
用户失败大量重试(整个服务雪崩)
失败的客户端多次重试,使得请求量急剧增加,产生雪崩现象
这种加剧是灾难式的,后台的过载保护不到位,使得前面的服务链过载,从而导致整个服务崩溃
可以看到,从宏观上看,过载需要防护的是三个点
超时请求处理的无用功
失败造成的重试,使得负载激增,导致过载
真的存在这么大的访问量,负载激增,导致过载
过载保护策略
服务的下游限流
根据QPS进行限制
把QPS限制在一个不高的水平,就能有效的防止过载现象
最简单的(错误的)算法
这种算法是直觉上的,一段时间设置一个计数器,如果计数器大于某个值,就返回繁忙,过了这一段时间就重置计数器
但是仔细一想问题就大了,例如限定qps是100,重置计数器的时间是1秒
然后在0.9-1秒时通过了100个请求,然后重置计数器
在1-1.1秒时又通过了100个请求,那么在这0.2秒内通过了200个请求,而不是限定的1秒内最多100个请求
这个算法的问题是无论你把时间间隔设置的多短,总有更短的单位时间会超出这个限定值,因为这种重置的设定不够平滑
平滑的算法已经很成熟了,漏桶和令牌桶
漏桶
假设请求到达时,往桶里加水,而桶会以恒定速度漏水,当加水太快,桶就满了,此时返回繁忙
令牌桶
令牌桶算法以一个恒定的速度往桶里放令牌,当请求到达时,从桶里取走一个令牌,当没有令牌可以取走时,返回繁忙
看别人的资料说漏桶和令牌桶算法是略有不同的,但是我觉得就是一个漏水一个反过来是进水,没看出来区别
优点:算法现成的,实现简单
缺点:QPS阀值的设定需要经验,更可能的是需要实际的性能测试,最关键的是一旦代码发生改动,需要重改
根据系统资源进行限制
系统资源包括:cpu,内存,磁盘,网络,根据系统资源的监控,判断达到阀值,此时返回繁忙
优点:不需要进行太多经验来设定,更不需要各种测试来设定限定值
缺点:有些情况下判断较为复杂,例如对一个自己管理内存的程序来说,内存吃满不一定是过载,他可能是自己申请了一大块内存去自己管理内存
或者对于多进程和多线程服务来说,单核的CPU跑满不一定是过载,但也有可能是过载。
超时机制
在协议中加入一个时间戳字段
客户端发送请求时记录下时间戳,当服务游进行处理时判断这个时间戳是否超过一定时间,如果超过,那就返回超时
优点:完全不需要任何配置,每个服务都可以配置相同的超时时间
缺点:
a 需要NTP同步,这倒不是什么大问题,crontab下就行了
b 对单个服务来说,这个请求可能没有超时,但是对整个链路来说,可能已经超时很久了
全链路超时机制
全链路超时是基于以上超时机制的补全
在整个调用链上,加上时间戳字段,由最前的接入层,打上时间戳,每一个下游服务在处理请求时都需要判断这个时间戳的是否超时
小结
限流策略主要还是防护无用功的问题,并不能解决另外两个问题
从功能性来看,限制QPS的算法和限制资源利用率,功能类似,但有不同的使用场景,并且可以和全链路超时机制一起用多加一层防护
负载均衡
负载均衡是锦上添花的策略
指的是上游调用方,当发现下游服务方的节点有些负载较高时,调低这个节点的权值,将请求调整到负载较低的实例上。
这能有效减少整个服务的过载可能性
这就需要一个下游到上游的反馈机制,来反馈负载情况
服务的上游限流
下游限流不能解决最关键的问题,下游虽然在一个劲的收拾垃圾,把超时的请求清理出去
但是架不住过载时,上游不停的发请求过来
这里就能利用负载均衡策略提供的反馈机制
当上游调用方,发现下游服务方的大部分节点都存在负载较高的情况,就直接开始丢弃部分请求,返回繁忙
优点:把问题掐死在较上游的位置
缺点:还不够彻底,当一个服务的调用链非常长时,其实整个调用链已经做了一大段无用功了
服务的接入层限流
这是上段限流的一个拓展
把反馈机制,一层层往上反馈,然后从接入层判断整个服务是否过载,来直接丢弃部分请求来返回繁忙
判断主要还是基于木桶效应,下游的服务哪个所有节点都负载较高,就判断整个服务负载较高,从而丢弃请求
总结
服务的下游限流策略解决了无用功的问题
服务的上游限流策略解决了负载激增时过载的问题,接入层限流策略作为一种扩展,能做的更好