简单说一下什么是负载均衡?不少人最怕这种概念性问题前端
大家公司负载均衡用的什么?git
为何用这种?github
它的优缺点面试
有更好的选择吗?算法
你说这5联问,谁受得了啊,丛浅到深,一环扣一环,简直不要了,别怕,仔细阅读本文,这些问题都会迎刃而解。apache
俗话解释一下负载均衡:你要在10个餐厅中选一个吃午饭,那么你选的这个过程就是负载均衡的过程,(面试也是能够这么说的)。
正规的行话:负载均衡指的是在一个集群中经过某种硬件设备或者软件算法来选择集群中的一台机器处理当前请求,以达到大量请求的分散给后端集群不一样机器处理,从而提高高并发能力和容灾能力。
百度百科:负载均衡创建在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增长吞吐量、增强网络数据处理能力、提升网络的灵活性和可用性后端
目前负载均衡总的来讲分为三大类:1 硬件设备负载均衡,2 软件算法负载均衡,3 基于DNS的负载均衡 分别介绍一下这三大类的不一样和优缺点。缓存
硬件负载均衡解决方案是直接在服务器和外部网络间安装负载均衡设备,这种设备一般称之为负载均衡器,因为专门的设备完成专门的任务,独立于操做系统,总体性能获得大量提升,加上多样化的负载均衡策略,智能化的流量管理,可达到最佳的负载均衡需求,其主要应用在大型服务器集群中,好比F5负载均衡器。服务器
软件负载均衡指的是在服务器的操做系统上安装负载均衡软件,今后服务器发出的请求经软件负载均衡算法路由到后端集群的某一台机器上。网络
DNS负载均衡通常用于地理位置上的负载均衡,好比你的网站在全国范围内都有海量用户,那么当不一样用户访问网站域名时通过DNS判断返回给不一样地理位置的用户的不一样IP,从而达到就近访问,流量分担,提高用户体验。
硬件负载均衡通常只是关注网络流量的负载,至于后端服务器的状态等他不操心,并且成本贵,每每也是单点,但它也有优势,就是性能好,处理能力强,与操做系统无关性。
软件负载均衡比较灵活,可调整性大,与软件算法实现有关系,可以关注应用服务器的状态作汇总统计试别的能力,性价比较高,但受软件安装的服务器性能影响,同时也没硬件的性能好,DNS负载均衡也属于软件负载均衡的一种。
本文主要分析的也是软件负载均衡。
负载均衡中间件如今不少,你们最熟悉的,也是最出名的就属Nginx了,其次也有不少,好比百度前段时间开源了bfe(百度统一前端),是百度7层流量转发平台,还有apache,各类微服务中间件中的负载均衡算法等
咱们主要分析下这些中间件负载均衡策略是怎么实现的?用的什么算法,重点来了
随机策略指的是在后端集群机器的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的加权轮询策略采用的就是第二种状况
最少链接策略会关注后端集群各个服务器当前的链接数,选择一个最少链接数的机器应答当前请求,这种策略实际上关注各个服务器的负载状况,选择负载最低的机器处理请求,尽量的提升各个机器的利用率,相对来讲比较灵活和智能,实现上也会复杂一些。
延迟感知策略和最少链接是同样的思想,延迟感知追求极致的性能或者说用户体验,老是挑选可以最快的返回执行结果的机器来访问,但坏处是当都全部客户端都认为某台服务器最快时,那么全部请求都发送这台服务反而可能形成服务压力过大,性能下降。
源地址散列策略可以让同一客户端的请求或者同一用户的请求老是请求在后端同一台机器上,这种算法根据客户端IP求出Hash值而后对端集群总数求余获得值就是服务器集合的下标,通常这种算法用于缓存命中,或者同一会话请求等,但这种算法也有必定的缺点,某一用户访问量(黑产)很是高时可能形成服务端压力过大或者后端服务Down掉,那么客户端就会没法访问,因此也须要必定的降级策略。
一致性散列是在源地址散列的基础上发展得来的,什么意思呢?后端集群有是个3台机器(a,b,c),客户端通过散列对服务器总数取余后老是请求到a机器,那么当后端集群新增或者减小一台机器时,客户端散列后对服务器总数取余后就再也不是原来的那台机器了,这样原来全部的请求散列后对应的后台机器都发生了变化,一致性散列就是解决这种问题的.
咱们挑选上面一种策略用代码来实现一下,以便让你们更深刻的理解,选择一个面试常问的策略,一、加权轮询算法,这个也比较多,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]
整个代码我已提交到github上,你们能够github上下载下来实际运行一下,加深理解,我得github地址以下:
https://github.com/sunpengwei1992/go_common/blob/master/algorithm/load_balance.go
任何一种算法深刻研究后都能引出一堆问题来,均可以单独写一篇文章出来,本篇重点是在让你们知道这些算法,以致于见到后不会陌生,须要你们在工做中不断探索,不断升级本身的认知,提升思惟能力。