做了「负载均衡」即可以随意加机器了吗?

作者 : 开心源码 本文共2896个字,预计阅读时间需要8分钟 发布时间: 2022-05-12 共176人阅读

下面这个场景不知能否在你面前出现过:

开发Z哥对运维Y弟喊:“Y弟,现在系统好卡,刚上了一波活动,赶紧帮我加几台机器上去顶一下。”

Y弟回复说:“没问题,分分钟搞定”。

而后就发现数据库的压力迅速上升,DBA就吼了:“Z哥,你丫的搞什么呢?数据库要被你弄垮了”。

而后客服那边接框也爆炸了,越来越多的客户说刚登陆后没多久,操作着就退出了,接着登陆,又退出了,究竟还做不做生意了。

这些问题背后都是因为一个「Session丢失」问题导致的。

一、什么是Session丢失

相信Session对大部分Coder来说应该都知道。它是为了将同一个客户的屡次访问在系统中被识别为“同一个客户”而产生的概念。除此之外,还可以基于它来减少重复往DB或者者远程服务处获取与该客户相关的信息,以起到提升性能的作用。

在我们做了负载均衡的场景中,假如选择的负载策略是hash策略,那么会使得Session产生一个反作用,这个反作用就如上面举的案例那样,客户一旦因为某种起因从原价访问服务器A变成访问服务器B,就会出现“登陆状态丢失”、“缓存穿透”等问题。

为什么hash策略会出现这个问题呢?首先有必要先理解一下hash是如何进行的。hash策略就是下图这样的一个散列函数。在函数不变的情况下,A永远对应01,B对应04,C对应08。

以nginx中的ip_hash策略来举个例子。由于我们认为正常情况下客户的ip不会在短时间内发生变化,所以当我们选择使用ip_hash策略进行负载均衡时,意味着期望同一个客户能够一直访问到同一台服务器上,就像下图这样,图中的hash函数是最简单的随便举例。

如此一来,我们只要要在这一台服务器上将这个客户相关的信息缓存在进程内,就能起到非常高性价比的提升性能的效果。

这时,用户端与服务端之间的相当于建立了一个信任,相互认识。这个信任就是「Session」。

但是,当我们加了一台服务器之后,事情就发生变化了,图中的hash函数是最简单的随便举例。

这个时候我们原价的预期就被破坏了。由于客户与序号0节点的链接变成了与序号3的链接,所以产生了前面提到的「Session丢失」问题。与此同时,在序号0节点上做的进程内缓存都无效了,而在序号3节点上又没有客户相关的任何缓存,导致大量数据需要从下游的DB或者者远程服务处获取。你要知道,一旦涉及到网络通信,性能必然显著下降,I/O、序列化都是耗时的工作。更重要的是,一旦同时有大量客户产生这个情况,因为后台的DB和远程服务瞬时无法承载激增的高密度请求,可能会导致它挂起。这还没完,假如当前程序没有少量故障隔离或者者降级策略,还会进一步产生蝴蝶效应,导致整个大系统响应缓慢。可谓“一颗老鼠屎坏了一锅粥”。

二、Nginx是如何来处理这个问题的

既然以nginx举例,还是从nginx开始聊。通过在nginx中引入nginx-sticky-module板块可以来处理这个问题。处理的整个过程如下。

可以看到,当client第一次进入到nginx匹配节点的时候,在给它分配一个节点的同时,会将这个节点的唯一标识进行md5后写入到cookie中一并返回,假如下次再发起请求的时候发现带有这个cookie值,就直接转发到该值所对应的节点上去。这个机制被专业的称之为「Session保持」。

尽管可以利用cookie来处理这个问题,但是cookie也有一个潜在的问题,假如用户端未开启cookie功能,这个机制就失效了。不过好在目前主流浏览器都是默认打开cookie的。

题外话:nginx是2004年发布的,在nginx-sticky-module出现之前的7年间也是nginx相比竞品HAProxy最大的一个短板,由于HAProxy支持Session保持。

三、Session保持的其它方案

除了cookie之外,还有2种方式也可以最终达到相似的效果。分别被称为「Session复制」、「Session共享」。

1、Session复制

这是最简单粗暴的方式。根据第一节的案例来看,导致问题的起因是节点3没有客户的Session。那么很容易想到,在节点3运行之前把Session相关的Cache数据复制过去呗。并且在多个节点之间持续保证数据的同步,也就是说,每一台节点上都存在每个客户的Session数据。

实现的方案有很多,特别是不同的宿主程序都或者多或者少提供了少量切入点,甚至是拿来即可使用的方案,如Tomcat的Delta Manager和Backup Manager、Tomcat和IIS的Filter机制等等,这里就不开展了。

此类方案的特点是:

优点:天然高可用,一部分节点宕机没事。由于每一个节点上存放着所有已连接客户的会话信息。

缺点:由于每台计算机的内存是有上限的,仅适用于会话相关的数据大小较小的场景。并且,因为多个节点之间需要同步数据,需要额外处理数据一致性问题。与此同时,随着节点越多,损耗越大(推迟、带宽等),有广播风暴风险。

2、Session共享

我们还可以通过将session信息存放到全局共享的存储介质中来达到一样的效果,如数据库、远程缓存等,这是一种中心化思想的处理方案。

此类方案的特点是

优点:不论节点怎样添加和减少,100%不会产生会话丢失。

缺点:每次读写请求都需要添加额外共享储存调用,添加了网络I/O、序列化等操作,性能显著下降。另外,用作共享的存储介质除了添加了额外的维护成本外,还需要处理单点问题,以免产生系统性风险。

同之前「Session保持」方案一起比照下各自的优缺点和适用场景。

分别用一句话概括一下这3个方案:

Session 保持。原来在哪还是去哪。

Session 复制。不论在哪都有一样的数据。

Session 共享。所有节点共用一份数据。

越大型的系统,最终都会往「Session共享」这个方案上走,由于只需再对这个共享存储做横向扩展,理论上即可以支撑无穷大的客户了。如Redis、一系列的NOSQL以及NEWSQL等。就像下面这样,集「规模大」、「高可用」、「效果好」于一身。

四、结语

现在你应该清楚了Session丢失问题,也知道了如何去应对他。但是,我们还需要明白一个事实:严格来说「Session保持」本质上是破坏了做「负载均衡」的初衷。举个极端点的场景:一共有10个会话连在了节点A上,并且都是活动中状态。那么这个时候哪怕添加一个节点B上线,只需没有新的会话进来,节点B上的活动连接数永远是0,并没有起到分担压力的作用。

但是,在系统的起步时期,其实用这样简单的方案也是极好的。

欢迎工作一到五年的Java工程师朋友们加入Java填坑之路:860113481

群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)正当利用自己每一分每一秒的时间来学习提升自己,不要再用”没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 做了「负载均衡」即可以随意加机器了吗?

发表回复