查看原文
其他

分布式 | 不可不知的负载均衡

田维常 Java后端技术全栈 2022-06-09

最近有小伙伴在后台留言,让我写一篇负载均衡的文章,说网上文章其实已经很多了,每次都觉得某某文章讲的不错,可是一旦过段时间,啥都不记得了。那今天我们就用生活中的故事来聊聊负载均衡。文章中部分可能有点啰嗦,但是为了更好能让大家理解,我也是拼了,真真切切的想让大家掌握知识。

点击获取「一份贴心的10万字面试小抄」

什么是负载均衡?

负载均衡,英文名称为Load Balance,其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器等,从而协同完成工作任务。

负载均衡通常有两种目的:均摊压力和提供冗余(也可以理解为备份)。

生活案列

上面还看不懂的话,我们继续用生活案列来说:

高速路出口处,如果只有一个出口时,突然有一天出现大量车辆(假设大家都没有办理ETC)这个高速出口下高速, 比如有几百两这会都要下高速,但是下高速要交过路费,每辆车至少也要耽搁几分钟,几百辆!!!意味着后面的可能要等几个小时,如果有多个出口呢?那就没必要等那么久了。

如果在增加一个出口,这时候就是两个出口可以均摊车辆下高速,还得分收费员快慢,车辆3看到车1那边要快点,然后就跟上车1。

如果再增加n个就可以想象效果了。但是太多了,貌似也会造成资源浪费,很多出口一天都没有几辆车出入,如果搞得太多岂不浪费,所以我们一般看到大多数都是两个,可以理解备用急用。

「我们就把司机理解为负载均衡器,可以根据前方路况进行判别走哪个出口。判别的方法就可以理解为负载均衡算法。」

用我们技术领域的术语叫做冗余。收费员的速度我就可以理解为我们系统某个服务的性能。

技术领域

下面用一张图来描述我们技术领域的负载均衡:

结合生活中的场景和技术领域的场景一起理解更酸爽。

注意:集群指的是我们同一个App应用服务的部署多个节点,集群的主要目的就是为了分担压力的。负载均衡器(系统)就可以理解为指挥员。来一个请求,指挥员把这个请求根据一定方法交给集群中的某个服务。指挥员就可以按照各种方式进行分配请求到集群中的某个服务。随机给、排队给、谁反应快给谁等方法,也就是形成了负载均衡算法。

以上比喻仅仅是个人理解。

负载均衡的种类

DNS

(Domain Name System 域名系统 )它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS使用TCP和UDP端口53。当前,对于每一级域名长度的限制是63个字符,域名总长度则不能超过253个字符。DNS是最简单也是最常见的负载均衡方式,一般用来实现“地理级别”的负载均衡,比如说:北方人访问北京的机房,南方人访问广州的机房,西方人访问成都的机房。DNS负载均衡的本质是DNS解析同一个域名可以返回不同的IP地址。比如说:https://www.sina.com.cn/在北方的用户使用时会解析成10.210.1.12(北京机房)返回,南方的用户使用时会解析成14.213.164.27返回(广州机房)。

DNS简单示意图

优点

  • 配置简单,无成本费用
  • 将负载均衡的工作交给了DNS服务器,省去了管理的麻烦。

缺点

  • 记录的添加与修改是需要一定时间才能够生效的(因为DNS缓存了A记录)。一旦有一台服务器坏了需要下线,即使修改了A记录,要使其生效也需要较长的时间,这段时间,DNS仍然会将域名解析到已下线的服务器上,最终导致用户访问失败。
  • 不能按需分配负载,DNS并不知道各服务器的真实负载情况,所以负载效果不是很好

实际的情况:在实际的项目部署,我们一般会将部分服务器使用DNS解析,利用域名解析作为第一级负载均衡.再在服务器中使用nginx负载均衡作为第二级负载均衡。

硬件负载均衡

硬件负载均衡是通过单独的设备来实现负载均衡的功能,这类设备和路由器交换机有那么一些类似,更或者可以理解为一个用于负载均衡的基础网络设备。目前业界主要有两款硬件负载均衡:F5和A10。这类设备性能好,功能强大,但是价格可以用昂贵来形容,一般只有银行,国企等大型有钱的企业开会考虑使用此类设备,本人也只是在银行里见识过F5。至于A10没接触过就不撤了。

优点

  • 功能强大:全面支持各层级的负载均衡,支持各种负载均衡算法,支持全局负载均衡。

  • 性能好:一般软件负载均衡能支撑10w+并发已经很不错了,但是硬件的负载均衡却可以支持100w+以上的并发。

  • 高稳定性:因为是商业品,所以经过了良好严格的测试,经过大规模的使用,所以稳定非常高。

  • 安全性高:硬件负载均衡设备除了能处理负载均衡以外,还具有防火墙、防DDOS攻击等效果。

缺点

  • 价格昂贵:我记得之前银行购买F5花了上百万,据说还有更贵的,所以价格可想而知。

  • 扩展性不好:硬件设备可以根据业务进行配置,但无法进行扩展和定制化。

软件负载均衡

软件负载均衡是通过负载均衡软件来实现负载均衡功能的。常见的负载均衡软件有LVS和Nginx。其中LVS是Linux内核的四层负载均衡,四层和七层的区别在于他们协议和灵活性的不同。Nginx是7层负载均衡,支持HTTP,E-mail协议,而LVS是四层负载均衡,所以和协议无关,基本上所有应用都可以做到,比如说:聊天、数据库等。

以下是Nginx的负载均衡简单示意图:

优点

  • nginx由C编写,同样的web服务器,占用的资源和内存低性能高。

  • 当启动nginx服务器,会生成一个master进程,master进程会fork出多个worker进程,由worker线程处理客户端的请求。

  • nginx支持高并发,每个worker子进程是独立平等的,当有客户端请求时,worker进程公平竞争,抢到的worker进程会把请求提交给后端服务器,当后端服务器没有及时响应时,此worker进程会继续接收下一个request,当上一个请求有响应后会触发事件,此worker进程继续之前的执行,知道响应结束。一个request不会被两个worker进程执行。

  • nginx支持反向代理(用户有感知的访问叫正向代理如使用vpn访问youtube,用户无感知访问叫反向代理如负载均衡),支持7层负载均衡(拓展负载均衡的好处)。

  • nginx是异步非阻塞型处理请求(第三点印证),采用的epollandqueue模式,apache是阻塞型处理请求。

  • nginx处理静态文件速度快(原因:

  • nginx高度模块化,配置简单。

  • nginx是单进程多线程)。

缺点

  • 对比apache不稳定,由于是单进程多线程,进程死掉会影响很多用户。

负载均衡有什么用?

  • 「流量分发」负载均衡能对多台主机流量进行分发,提高用户系统的业务处理能力,提升服务可用性
  • 「会话保持」在会话周期内,会话保持可使来自同一IP或网段的请求被分发到同一台后端服务器上。
  • 「健康检查」支持自定义健康检查方式和频率,可定时检查后端主机运行状态,提供故障转移,实现高可用;
  • 「负载均衡」解决并发压力,提高应用处理性能(增加吞吐量,加强网络处理能力);
  • 提高扩展性通过添加或减少服务器数量,提供网站伸缩性(扩展性);
  • 提高安全性安全防护,在负载均衡器上做一些过滤,黑白名单、防盗链等处理;

常用负载均衡算法

轮训

负载均衡系统接收到请求后,按照一定顺序将请求分发给服务器上。轮训是一种简单的负载均衡算法策略,不会去关注服务器状态。

优点:如果服务器都是正常的,那么轮训是最理想的,因为它会使得每个服务都得到相等量的请求,可以用"雨露均沾"来形容。

缺点:上面的有点是理想状态的,但是现实往往不是那样的,现实还是很骨感滴,线上系统往往出现各种各样的问题,比如:当有一台服务器挂了,轮训算法不会管服务器状态,就是会导致大量的请求到一台已经挂掉的服务器上,从而导致系统不可用,进而造成用户流失。另外一种常见的问题就是有的服务器响应快,有的响应慢(比如32核的服务器和16核的服务器),轮训算法也不关注相应快慢,所以会导致很多服务请求响应时间慢,简单的导致用户体验不好,由于响应时间慢甚至可能拖垮其他系统。

加权轮训

负载均衡系统根据服务器权重进行请求任务分派到对应的服务器上,这里的权重一般是根据系统硬件配置进行静态配置的,采用动态的方式计算会更加适合业务,但是复杂度相比简单的轮训就高很多。

加权轮训是轮训的一种特殊方式,主要目的是解决服务器处理能力的差异问题,比如:集群中有的服务器是32核,有的老系统却是16核,那么理论上我们可以对其进行权重配置值,即就是32核服务器的处理能力是16核的两倍,负载均衡算法权重比例调整为2:1,让更多的请求分发给32核的服务器。

加权轮训解决了轮训算法中误服根据服务器的配置的差异任务进行更好的分配的问题,其实还是会存在无法根据服务器的状态差异性进行请求任务分配的问题。

负载最低优先

负载系统将请求分配给当前负载最低的服务器,这里的负载根据不同请求类型和业务处理场景,可以用不同的指标来衡量。比如以下几个场景,

  • LVS这种4层网络负载均衡设备,可以以连接数来判断服务器的状态,服务器连接数量越大,表明服务器压力就越大。
  • Nginx这种7层网络负载均衡系统,可以以HTTP请求数量判断服务器的状态(Nginx内置的负载均衡算法不支持这种方式,需要自行进行扩展)。
  • 如果我们是自己研发负载均衡系统,可以根据业务特点来选择衡量系统压力的指标。如果CPU是密集型,可以以CPU负载来衡量系统的压力;如果是IO密集型,则可以以IO负载来衡量系统压力。

负载最低优先算法解决了轮训算法中无法感知服务器状态的问题,但是由此带来的代价是复杂度增加很多,比如:

  • 最少链接数优先的算法要求负载系统统计每个服务器当前简历的链接,其应用场景仅限于负载均衡接收的任何请求都会转发给服务器进行处理,否则如果负载均衡系统和服务之间是固定的连接池方式,就不适合采取这种算法。LVS可以采取这种算法进行负载均衡,而一个通过连接池的方式链接数据库Mysql集群的负载均衡系统就不适合采取这种算法进行负载均衡了。
  • CPU负载均衡最低优先的算法要求负载均衡系统以某种方式收集每个服务器的CPU的具体负载情况,同时要确定是以一分钟的负载标准,还是以10分钟、15分钟的负载标准,不存在1分钟肯定比15分钟的好或差。不同业务最优的时间间隔也是不一样的,时间间隔太短容易造成频繁波动,时间太长可能造成峰值来临时响应缓慢。

负载最低优先的算法基板上能够很完美解决了轮训算法的缺点,也因为采用负载最低优先算法后,负载均衡系统需要感知服务器当前运行状态,此时,同样造成代价上升很多。对于开发者来说也许轮训算法只要简短的代码就可以实现,然而负载最低优先算法需要大量的代码来实现。

负载最低优先看起来是解决了轮训中的缺点,然后由于其复杂度的提升,导致真正使用中比例还不如轮训或者轮训加权算法。

性能最优

负载最低优先算法是站在服务器的角度来进行请求分配的,而性能最优算法是站在客户端的角度进行分配的,优先将请求分配给处理速度快的服务器,通过这种方式达到了最快响应给客户端。

性能优先其实也负载最低优先有点类似,都是需要感知服务器的状态,与之不同的是性能最优是通过响应时间这个标准,在外部进行感应服务器状态而已,同样的实现复杂度也很高,主要体现在以下方面:

  • 负载均衡系统需要收集每次请求的响应时间,如果在大量请求处理的场景下,这种收集再加上响应时间的统计本身也会消耗系统的性能。
  • 为了减少这种统计上的消耗,可以采取采样的方式进行统计,即就是不用很完全的去统计所有服务器的所有请求时间,而是抽样统计部分任务的响应时间来估算整体请求所花的响应时间。采样统计虽然能减轻性能的消耗,但使得实现的复杂度增加了很多,因为要确定合适的采样率,采样率太低会导致数据的正确性,采样率高同样会造成性能的消耗,要找到一个合适的采样率的复杂度也是可想而知的。
  • 无论全部统计,还是采样统计,都需要选择合适的周期,是30秒性能最优还是1分钟最优?目前是没有标准的周期,都是需要具体业务场景进行决策,是不是感觉到了其复杂性,尤其是线上系统需要不断的调试,然后找出相对合适的标准。
Hash类

负载均衡系统根据请求中某些关键字进行hash运算,得到的相同值得分发到同一台服务器上去,这样做的目的主要是为了满足特定的业务需求,比如:

  • 源地址Hash:将来源于同一个IP地址的请求分配给同一个服务器进行处理,适合于存在事务、会话的业务。例如:当我们通过浏览器登录网上银行时,会生成一个会话信息,这个会话是临时的,关闭浏览器后就会失效。网上银行后台无须持久会话信息,只需要在某台服务器临时保留这个会话就可以了,但需要保证用户在会话存在期间,每次请求都能访问在同一个服务器,这种业务场景就是通过源地址hash来实现的。
  • ID hash :将某个ID表示的业务分配到同一台服务器上进行处理,比如:userId session id。上述的网上银行登录的例子,用session id hash可以实现同一个会话期间,用户每次都是访问同一台服务器上的目的。

负载均衡算法应用

Dubbo中使用了哪些负载均衡算法?
  • Random LoadBalance(随机算法,默认)
  • RoundRobin LoadBalance(权重轮训算法)
  • LeastAction LoadBalance(最少活跃调用数算法)
  • ConsistentHash LoadBalance(一致性Hash法)

类图

nginx中使用了哪些负载均衡算法?

「round robin(默认)」:轮询方式,依次将请求分配到各个后台服务器中,默认的负载均衡方式。适用于后台机器性能一致的情况。挂掉的机器可以自动从服务列表中剔除。

「weight」:根据权重来分发请求到不同的机器中,指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。 例如:

upstream bakend {    
server 192.168.0.14 weight=10;    
server 192.168.0.15 weight=10;    
}  

「IP_hash」:根据请求者ip的hash值将请求发送到后台服务器中,可以保证来自同一ip的请求被打到固定的机器上,可以解决session问题。例如:

upstream bakend {    
ip_hash;    
server 192.168.0.14:88;    
server 192.168.0.15:80;    
}   

「url_hash(第三方)」:根据请求的url的hash值将请求分到不同的机器中,当后台服务器为缓存的时候效率高。

例如:在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法 。

「fair(第三方)」:根据后台响应时间来分发请求,响应时间短的分发的请求多。例如:

upstream backend {    
server server1;    
server server2;    
fair;    
}  

总结

我们用生活中的故事来讲述了负载均衡,讲述了什么是负载均衡,负载均衡的作用,负载均衡的种类,负载均衡算法种类,以及我们在Dubbo和nginx中负载均衡算法的应用。

希望老铁能get到点!如有疑问或什么建议的加我微信tj20120622我们慢慢聊。直到让大家理解掌握为止。

「只要我们的方向对了,就不怕路远!」

推荐阅读

面试:Zookeeper常见11个连环炮
有这本面试小抄,金三银四心里有底儿了!
面试:说说几个常见的Linux性能调优命令
《程序员面试宝典》.pdf下载
JVM真香系列:如何判断对象是否可被回收?

码字不易,点个赞呗

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存