转自:2016-09-20 58沈剑 架构师之路
零、需求缘起
在这两篇文章中,都强调了“负载均衡是指,将请求
/
数据【均匀】分摊到多个操作单元上执行,
负载均衡的关键在于【均匀】
”。
然而,后端的
service
有可能部署在硬件条件不同的服务器上
:
1)如果对标最低配的服务器“均匀”分摊负载,高配的服务器的利用率不足;
2)如果对标最高配的服务器“均匀”分摊负载,低配的服务器可能会扛不住;
能否根据异构服务器的处理能力来动态、自适应进行负载均衡及过载保护,是本文要讨论的问题
。
一、
service
层的负载均衡通常是怎么做的
![](https://i-blog.csdnimg.cn/blog_migrate/5c2ac4bba3992cac2eee6d61cc55a31e.png)
“
一分钟了解负载均衡
”中提到,
service
层的负载均衡,一般是通过
service
连接池来实现的
,调用方连接池会建立与下游服务多个连接,每次请求“随机”获取连接,来保证
service
访问的均衡性。
这个
调用方连接池能否实现,根据
service
的处理能力,动态
+
自适应的进行负载调度呢?
二、通过“静态权重”标识
service
的处理能力
调用方通过连接池组件访问下游
service
,通常采用“随机”的方式返回连接,以保证下游
service
访问的均衡性。
要打破这个随机性,最容易想到的方法,只要为每个下游
service
设置一个“权重”,代表
service
的处理能力,来调整访问到每个
service
的概率
,例如:
假设
service-ip1
,
service-ip2
,
service-ip3
的处理能力相同,可以设置
weight1=1
,
weight2=1
,
weight3=1
,这样三个
service
连接被获取到的概率分别就是
1/3
,
1/3
,
1/3
,能够保证均衡访问。
假设
service-ip1
的处理能力是
service-ip2
,
service-ip3
的处理能力的
2
倍,可以设置
weight1=2
,
weight2=1
,
weight3=1
,这样三个
service
连接被获取到的概率分别就是
2/4
,
1/4
,
1/4
,能够保证处理能力强的
service
分别到等比的流量,不至于资源浪费。
使用
nginx
做反向代理与负载均衡,就有类似的机制。
这个方案的
优点
是:
简单
,能够快速的实现异构服务器的负载均衡。
缺点
也很明显:这个
权重是固定的,无法自适应动态调整
,而很多时候,服务器的处理能力是很难用一个固定的数值量化。
三、通过“动态权重”标识
service
的处理能力
提问:通过什么来标识一个
service
的处理能力呢?
回答:
其实一个
service
能不能处理得过来,能不能响应得过来,应该由调用方说了算。
调用服务,快速处理了,处理能力跟得上;调用服务,处理超时了,处理能力很有可能跟不上了。
动态权重设计
1
)用一个动态权重来标识每个
service
的处理能力,默认初始处理能力相同,即分配给每个
service
的概率相等;
2
)
每当
service
成功处理一个请求,认为
service
处理能力足够,权重动态
+1
3
)
每当
service
超时处理一个请求,认为
service
处理能力可能要跟不上了,权重动态
-10
(权重下降会更快)
4
)为了方便权重的处理,可以把权重的范围限定为
[0, 100]
,把权重的初始值设为
60
分
举例说明:
假设
service-ip1
,
service-ip2
,
service-ip3
的动态权重初始值
weight1=weight2=weight3=60
,刚开始时,请求分配给这
3
台
service
的概率分别是
60/180
,
60/180
,
60/180
,即负载是均衡的。
随着时间的推移,处理能力强的
service
成功处理的请求越来越多,处理能力弱的
service
偶尔有超时,随着动态权重的增减,权重可能变化成了
weight1=100
,
weight2=60
,
weight3=40
,那么此时,请求分配给这
3
台
service
的概率分别是
100/200
,
60/200
,
40/200
,即处理能力强的
service
会被分配到更多的流量。
四、过载保护
提问:什么是过载保护?
图示:
无
过载保护的负载与处理能力图(会掉底
)
回答
:互联网软件架构设计中所指的过载保护,是指当系统负载超过一个
service
的处理能力时,
如果
service
不进行自我保护,可能导致对外呈现处理能力为
0
,且不能自动恢复
的现象。而
service
的
过载保护
,是指即使系统负载超过一个
service
的处理能力,
service
让能保证对外提供有损的稳定服务。
图示:
有
过载保护的负载与处理能力图
(不会掉底)
提问:如何进行过载保护?
回答
:最简易的方式,
服务端
设定一个负载阈值,
超过这个阈值的请求压过来,全部抛弃
。这个方式不是特别优雅。
五、如何借助“动态权重”来实施过载保护
动态权重是用来标识每个
service
的处理能力的一个值,它是
RPC-client
客户端
连接池层面的一个东东。服务端处理超时,客户端
RPC-client
连接池都能够知道,这里只要
实施一些策略,就能够对“疑似过载”的服务器进行降压
,而不用服务器“抛弃请求”这么粗暴的实施过载保护。
应该实施一些什么样的策略呢,例如:
1
)如果某一个
service
的连接上,
连续
3
个请求都超时
,即连续
-10
分三次,客户端就可以认为,服务器慢慢的要处理不过来了,得
给
这
个
service
缓一小口气
,于是设定策略:接下来的若干时间内,例如
1
秒(或者接下来的若干个请求),请求不再分配给这个
service
;
2
)如果某一个
service
的
动态权重,降为了
0
(像连续
10
个请求超时,中间休息了
3
次还超时),客户端就可以认为,服务器完全处理不过来了,得
给这个
service
喘一大口气
,于是设定策略:接下来的若干时间内,例如
1
分钟(为什么是
1
分钟,根据经验,此时
service
一般在发生
fullGC
,差不多
1
分钟能回过神来),请求不再分配给这个
service
;
3
)可以有更复杂的保护策略
…
这样的话,不但能借助“动态权重”来实施动态自适应的异构服务器负载均衡,还能在客户端层面更优雅的实施过载保护,在某个下游
service
快要响应不过来的时候,给其喘息的机会。
需要注意的是
:要防止客户端的过载保护引起
service
的雪崩,
如果“整体负载”已经超过了“
service
集群”的处理能力,怎么转移请求也是处理不过来的,还得通过抛弃请求来实施自我保护
。
六、总结
1
)
service
的负载均衡
、故障转移、超时处理
通常是
RPC-client
连接池层面来实施的
2
)异构服务器负载均衡,最简单的方式是
静态权重法
,缺点是无法自适应动态调整
3
)
动态权重法
,可以动态的根据
service
的处理能力来分配负载,需要有连接池层面的微小改动
4
)
过载保护
,是在负载过高时,
service
为了保护自己,保证一定处理能力的一种自救方法
5
)
动态权重法,还可以用做
service
的过载保护