分布式学习-Session丢失及解决

什么是Session丢失

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

在做了负载均衡的场景中,如果选择的负载策略是hash策略,那么会使得Session产生一个副作用:用户一旦由于某种原因从原先访问服务器A变成访问服务器B,就会出现“登陆状态丢失”、“缓存穿透”等问题。

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

以nginx中的ip_hash策略来举个例子。正常情况下用户的ip不会在短时间内发生变化,所以当使用ip_hash策略进行负载均衡时,意味着期望同一个用户能够一直访问到同一台服务器上,就像下图这样。
img

如此一来,我们只需要在这一台服务器上将这个用户相关的信息缓存在进程内,这时,客户端与服务端之间的相当于建立了一个信任,相互认识。这个信任就是「Session」。但是,当我们加了一台服务器之后,事情就发生变化了。
img

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

nginx是如何解决的

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

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

其他解决方案

Session复制

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

实现的方案有很多,特别是不同的宿主程序都或多或少提供了一些切入点,甚至是拿来即用的方案,如Tomcat的Delta Manager和Backup Manager、Tomcat和IIS的Filter机制等等。此类方案的特点是

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

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

Session共享

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

此类方案的特点是:

优点:不管节点怎么增加和减少,100%不会产生会话丢失。

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

三种方案对比

img

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

Session 保持:原来在哪还是去哪。
Session 复制:不管在哪都有一样的数据。
Session 共享:所有节点共用一份数据。

本文标题:分布式学习-Session丢失及解决

文章作者:王洪博

发布时间:2019年05月27日 - 10:05

最后更新:2019年09月12日 - 10:09

原始链接:http://whb1990.github.io/posts/223e2ff2.html

▄︻┻═┳一如果你喜欢这篇文章,请点击下方"打赏"按钮请我喝杯 ☕
0%