本文长度为3056字,预计读完需1.1MB流量,建议阅读8分钟。nginx
这篇是《分布式关注点系列》中「负载均衡」相关的内容最后一发了,后续也会继续讲「高可用」相关的其它主题,主要是限流、降级、熔断之类的吧,具体还没定。文末先附上以前发过的高可用相关文章,供你再温故一下。数据库
下面这个场景不知是否在你面前出现过。后端
开发Z哥对运维Y弟喊:“Y弟,如今系统好卡,刚上了一波活动,赶忙帮我加几台机器上去顶一下。”浏览器
Y弟回复说:“没问题,分分钟搞定”。缓存
而后就发现数据库的压力迅速上升,DBA就吼了:“Z哥,你丫的搞什么呢?数据库要被你弄垮了”。服务器
而后客服那边接框也爆炸了,愈来愈多的用户说刚登录后没多久,操做着就退出了,接着登录,又退出了,到底还作不作生意了。微信
这些问题背后都是因为一个「Session丢失」问题致使的。cookie
相信Session对大部分Coder来讲应该都知道。它是为了将同一个用户的屡次访问在系统中被识别为“同一个用户”而产生的概念。除此以外,还能够基于它来减小重复往DB或者远程服务处获取与该用户相关的信息,以起到提高性能的做用。网络
在咱们作了负载均衡的场景中,若是选择的负载策略是hash策略,那么会使得Session产生一个反作用,这个反作用就如上面举的案例那样,用户一旦因为某种缘由从原先访问服务器A变成访问服务器B,就会出现“登录状态丢失”、“缓存穿透”等问题。session
为何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-sticky-module模块能够来解决这个问题。解决的整个过程以下。
▲图片来源于网络,版本归原做者全部
能够看到,当client第一次进入到nginx匹配节点的时候,在给它分配一个节点的同时,会将这个节点的惟一标识进行md5后写入到cookie中一并返回,若是下次再发起请求的时候发现带有这个cookie值,就直接转发到该值所对应的节点上去。这个机制被专业的称之为「Session保持」。
虽然能够利用cookie来解决这个问题,可是cookie也有一个潜在的问题,若是客户端未开启cookie功能,这个机制就失效了。不过好在目前主流浏览器都是默认打开cookie的。
题外话:nginx是2004年发布的,在nginx-sticky-module出现以前的7年间也是nginx相比竞品HAProxy最大的一个短板,由于HAProxy支持Session保持。
除了cookie以外,还有2种方式也能够最终达到相似的效果。分别被称为「Session复制」、「Session共享」。
这是最简单粗暴的方式。根据第一节的案例来看,致使问题的缘由是节点3没有用户的Session。那么很容易想到,在节点3运行以前把Session相关的Cache数据复制过去呗。而且在多个节点之间持续保证数据的同步,也就是说,每一台节点上都存在每一个用户的Session数据。
实现的方案有不少,特别是不一样的宿主程序都或多或少提供了一些切入点,甚至是拿来即用的方案,如Tomcat的Delta Manager和Backup Manager、Tomcat和IIS的Filter机制等等,这里就不展开了。
此类方案的特色是
优势:自然高可用,一部分节点宕机没事。由于每个节点上存放着全部已链接用户的会话信息。
缺点:由于每台计算机的内存是有上限的,仅适用于会话相关的数据大小较小的场景。而且,因为多个节点之间须要同步数据,须要额外解决数据一致性问题。与此同时,随着节点越多,损耗越大(延迟、带宽等),有广播风暴风险。
咱们还能够经过将session信息存放到全局共享的存储介质中来达到同样的效果,如数据库、远程缓存等,这是一种中心化思想的解决方案。
此类方案的特色是
优势:无论节点怎么增长和减小,100%不会产生会话丢失。
缺点:每次读写请求都须要增长额外共享储存调用,增长了网络I/O、序列化等操做,性能明显降低。另外,用做共享的存储介质除了增长了额外的维护成本外,还须要解决单点问题,以避免产生系统性风险。
同以前「Session保持」方案一块儿对比下各自的优缺点和适用场景。
分别用一句话归纳一下这3个方案:
Session 保持。原来在哪仍是去哪。
Session 复制。无论在哪都有同样的数据。
Session 共享。全部节点共用一份数据。
越大型的系统,最终都会往「Session共享」这个方案上走,由于只要再对这个共享存储作横向扩展,理论上就能够支撑无穷大的用户了。如Redis、一系列的NOSQL以及NEWSQL等。就像下面这样,集「规模大」、「高可用」、「效果好」于一身。
如今你应该清楚了Session丢失问题,也知道了如何去应对他。可是,咱们还须要明白一个事实:严格来讲「Session保持」本质上是破坏了作「负载均衡」的初衷。举个极端点的场景:一共有10个会话连在了节点A上,而且都是活动中状态。那么这个时候哪怕增长一个节点B上线,只要没有新的会话进来,节点B上的活动链接数永远是0,并无起到分担压力的做用。
可是,在系统的起步时期,其实用这样简单的方案也是极好的。
相关文章:
▶ 关于做者:张帆(Zachary, 我的微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。
微信公众号(首发):跨界架构师。<-- 点击后阅读热门文章
按期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些深度思考。