持续创作,加速生长!这是我参加「掘金日新计划 10 月更文应战」的第6天,点击查看活动详情
简单说一下什么是负载均衡?很多人最怕这种概念性问题
1.你们公司负载均衡用的什么?
2.为什么用这种?
3.它的优缺点
4.有更好的挑选吗?
你说这5联问,谁受得了啊,丛浅到深,一环扣一环,简直不要了,别怕,仔细阅读本文,这些问题都会迎刃而解。
什么是负载均衡?
俗话解释一下负载均衡:你要在10个餐厅中选一个吃午饭,那么你选的这个过程便是负载均衡的过程,(面试也是能够这么说的)。
正规的行话:负载均衡指的是在一个集群中经过某种硬件设备或许软件算法来挑选集群中的一台机器处理当时恳求,以达到很多恳求的涣散给后端集群不同机器处理,然后提升高并发才能和容灾才能。
百度百科:负载均衡建立在现有网络结构之上,它供给了一种廉价有用透明的办法扩展网络设备和服务器的带宽、添加吞吐量、加强网络数据处理才能、进步网络的灵敏性和可用性
软硬件负载均衡详解
现在负载均衡总的来说分为三大类:1 硬件设备负载均衡,2 软件算法负载均衡,3 基于DNS的负载均衡 别离介绍一下这三大类的不同和优缺点。
硬件负载均衡解决方案是直接在服务器和外部网络间安装负载均衡设备,这种设备通常称之为负载均衡器,由于专门的设备完成专门的使命,独立于操作系统,全体功能得到很多进步,加上多样化的负载均衡战略,智能化的流量办理,可达到最佳的负载均衡需求,其首要应用在大型服务器集群中,比方F5负载均衡器。
软件负载均衡指的是在服务器的操作系统上安装负载均衡软件,从此服务器发出的恳求经软件负载均衡算法路由到后端集群的某一台机器上。
DNS负载均衡一般用于地理位置上的负载均衡,比方你的网站在全国范围内都有海量用户,那么当不同用户拜访网站域名时经过DNS判断回来给不同地理位置的用户的不同IP,然后达到就近拜访,流量分担,提升用户体会。
他们的优缺点是什么呢?
硬件负载均衡一般仅仅重视网络流量的负载,至于后端服务器的状况等他不操心,而且成本贵,往往也是单点,但它也有优点,便是功能好,处理才能强,与操作系统无关性。
软件负载均衡比较灵敏,可调整性大,与软件算法完成有关系,能够重视应用服务器的状况做汇总计算试别的才能,性价比较高,但受软件安装的服务器功能影响,同时也没硬件的功能好,DNS负载均衡也属于软件负载均衡的一种。
本文首要分析的也是软件负载均衡。
常用的负载均衡算法和完成原理
负载均衡中间件现在很多,咱们最了解的,也是最知名的就属Nginx了,其次也有很多,比方百度前段时间开源了bfe(百度一致前端),是百度7层流量转发渠道,还有apache,各种微服务中间件中的负载均衡算法等
咱们首要分析下这些中间件负载均衡战略是怎样完成的?用的什么算法,要点来了
- Random 随机
- Round Robin 轮询
- Weighted Round Robin 加权轮询
- Least Connections 最少衔接
- Latency-Aware 推迟感知(最小推迟,也便是说那台机器功能最好,就用那台)
- Source Hashing 源地址散列
- Consistency hash 一致性散列(一般在分布式缓存中比较常见 )
随机战略指的是在后端集群机器的IP列表中依据随机数挑选一个IP作为此次恳求的应答者,当随机算法足够好,足够公平时,在海量恳求下,最终后端集群各个机器承载的流量是均衡, 随机战略会导致装备较低的机器Down机,然后或许引起雪崩,一般选用随机算法时建议后端集群机器装备最好平等的,随机战略的功能取决与随机算法的功能。
轮询战略指的是在集群中对一切机器编号,假设10台机器,从0-9,恳求来暂时从0号机器开始,后续每来一次恳求对编号加1,这样一向循环,上面的随机战略其实最终就变成轮询了,这两种战略都不关心机器的负载和运行状况,而且对变量操作会引入锁操作,功能也会下会下降。
加权轮询战略指的是回给后端集群每台机器都分配一个权重,权重高得会承当更多的流量,相反权重低的分配的流量也会少,这种战略答应后端集群机器装备差异化,假设有3台机器(a,b,c),他们的权重别离是(7,2,1),那么10次恳求a机器承当7次,b机器承当2次,c机器承当1次,可是这种承当法究竟怎样分配呢?有两种状况如下,咱们能够看到第一种恳求在a的时候,bc完全闲暇,而第二种状况相对均匀一些,Nginx的加权轮询战略选用的便是第二种状况:
- (aaaaaaa,bb,c)
- (aabaabaaca)
最少衔接战略会重视后端集群各个服务器当时的衔接数,挑选一个最少衔接数的机器应答当时恳求,这种战略实际上重视各个服务器的负载状况,挑选负载最低的机器处理恳求,尽或许的进步各个机器的利用率,相对来说比较灵敏和智能,完成上也会杂乱一些。
推迟感知战略和最少衔接是一样的思维,推迟感知寻求极致的功能或许说用户体会,总是挑选能够最快的回来履行成果的机器来拜访,但害处是当都一切客户端都认为某台服务器最快时,那么一切恳求都发送这台服务反而或许形成服务压力过大,功能降低。
源地址散列战略能够让同一客户端的恳求或许同一用户的恳求总是恳求在后端同一台机器上,这种算法依据客户端IP求出Hash值然后对端集群总数求余得到值便是服务器集合的下标,一般这种算法用于缓存射中,或许同一会话恳求等,但这种算法也有一定的缺点,某一用户拜访量(黑产)十分高时或许形成服务端压力过大或许后端服务Down掉,那么客户端就会无法拜访,所以也需求一定的降级战略。
一致性散列是在源地址散列的基础上开展得来的,什么意思呢?后端集群有是个3台机器(a,b,c),客户端经过散列对服务器总数取余后总是恳求到a机器,那么当后端集群新增或许减少一台机器时,客户端散列后对服务器总数取余后就不再是本来的那台机器了,这样本来一切的恳求散列后对应的后台机器都发生了改变,一致性散列便是解决这种问题的.
完成一个负载均衡算法
咱们挑选上面一种战略用代码来完成一下,以便让咱们更深化的理解,挑选一个面试常问的战略,1、加权轮询算法,这个也比较多,Nginx中默认的算法
加权轮询算法每台服务器有三个权重:初始装备的权重,当时权重,有用权重,其中初始装备权重和有用权重是不变的,默认状况有用权重等于初始装备权重,当装备文件的初始装备权重改动时,会触发有用权重改动,只要当时权重是动态改变的。
每次恳求到来时都从服务器列表中挑选一个当时权重最高的,之后将挑选出来的服务器当时权重减去一切服务器权重的和重新赋值给该服务器当时权重,这总算法经过不断递减当时权重使得一切服务器都有时机服务恳求,比较滑润,代码完成如下
首先界说一个结构体,加权轮询算法的中心要素必须有服务器初始装备权重,当时权重(权重在实际运行时或许发生改变)
type SeverWeight struct {
//装备的权重
ConfigWeight int
//当时权重
CurrentWeight int
//有用权重(值等于ConfigWeight,不过该字段是用一个装备特点,供前端修改使用)
EffectiveWeight int
//服务器ip
Ip string
}
//加权轮询算法
type WeightedRoundRobin struct {
//机器ip和对应的权重
IpAndWeightedConfig map[string]int
//服务器和权重信息
SwSlice []*SeverWeight
}
依据装备信息创立担任均衡对象,初始化各个字段的值
//初始化加权轮询对象
func NewWeightedRoundRobin(iwc map[string]int) *WeightedRoundRobin {
if iwc == nil {
return nil
}
SwSlice := make([]*SeverWeight, 0)
for k, v := range iwc {
sw := &SeverWeight{ConfigWeight: v, CurrentWeight: 0,
EffectiveWeight: v, Ip: k}
SwSlice = append(SwSlice, sw)
}
return &WeightedRoundRobin{IpAndWeightedConfig: iwc, SwSlice: SwSlice}
}
这个办法是中心,调用这个办法来决议挑选哪个服务器供给服务,办法的中心逻辑是挑选当时权重最大的服务器供给服务,当时权重不断在改变,每次当时权重的值都等于当时值加上有用值减去一切服务器的有用权重和(这个算法便是不断递减当时服务器的当时权重值,使得依照均匀的改变让一切服务器都能供给服务)
func (wrr *WeightedRoundRobin) Select() (sw *SeverWeight) {
total := 0 //计算一切服务器权重和
for _, v := range wrr.SwSlice { //遍历服务器
//当时权重加上有用权重
v.CurrentWeight += v.EffectiveWeight
total += v.EffectiveWeight
//当装备值修改的时候的,有用权重按部就班的添加
if v.EffectiveWeight < v.ConfigWeight {
v.EffectiveWeight++
}
//把权重最大的赋值给sw(sw是需求回来的对象)
if sw == nil || v.CurrentWeight > sw.CurrentWeight {
sw = v
}
}
//当时回来对象的权重-一切服务器权重和
sw.CurrentWeight = sw.CurrentWeight - total
return sw
}
咱们再来看一下履行的测验成果,依据测验成果相信咱们就能够明白了,依据下面成果咱们的确能够看到回来的服务器IP是均匀的,比较滑润,不会让权重低的服务器一向等待。
func TestNewWeightedRoundRobin(t *testing.T) {
//服务器ip和权重装备
config :=map[string]int{"10.1": 7, "10.2": 2, "10.3": 1}
wrr := NewWeightedRoundRobin(config)
//发送10次恳求
for i := 0; i < 10; i++ {
sw := wrr.Select()
t.Log(sw.Ip)//打印每次恳求IP
}
}
//成果:[10.1,10.1,10.2,10.1,10.1,10.3,10.1,10.1,10.2,10.1]
整个代码如下,咱们能够拷贝一份实际运行一下,加深理解:
package algorithm
type SeverWeight struct {
//装备的权重
ConfigWeight int
//当时权重
CurrentWeight int
//有用权重
EffectiveWeight int
//服务器ip
Ip string
}
//加权轮询算法
type WeightedRoundRobin struct {
//机器ip和对应的权重
IpAndWeightedConfig map[string]int
//服务器和权重信息
SwSlice []*SeverWeight
}
func NewWeightedRoundRobin(iwc map[string]int) *WeightedRoundRobin {
if iwc == nil {
return nil
}
SwSlice := make([]*SeverWeight, 0)
for k, v := range iwc {
sw := &SeverWeight{ConfigWeight: v, CurrentWeight: 0, EffectiveWeight: v, Ip: k}
SwSlice = append(SwSlice, sw)
}
return &WeightedRoundRobin{IpAndWeightedConfig: iwc, SwSlice: SwSlice}
}
func (wrr *WeightedRoundRobin) Select() (sw *SeverWeight) {
total := 0
for _, v := range wrr.SwSlice {
v.CurrentWeight += v.EffectiveWeight
total += v.EffectiveWeight
if v.EffectiveWeight < v.ConfigWeight {
v.EffectiveWeight++
}
if sw == nil || v.CurrentWeight > sw.CurrentWeight {
sw = v
}
}
sw.CurrentWeight = sw.CurrentWeight - total
return sw
}
//一致性hash算法
type ConsistencyHash struct {
}
任何一种算法深化研究后都能引出一堆问题来,都能够单独写一篇文章出来,本篇要点是在让咱们知道这些算法,以至于见到后不会生疏,需求咱们在工作中不断探索,不断升级自己的认知,进步思维才能。