讨论Load balancing中的session persistence
前言
这段时间一直在思考实现一个针对HTTP协议的LB该考虑哪些要素。其中一个特性令我深思,那就是会话持久
(session persistence)该如何保证?
之所以关注该特性,那必然是因为缓存的信息多是和用户、业务直接相关的,一旦缓存失效,那么就会导致用户体验下滑,比如莫名其妙地回到了登录界面;另外,再次缓存也会导致对性能等因素造成负面影响。
总之,各种意义上来说,不能说是个可以忽视的特性。
一致性哈希
要实现均衡负载中会话持久,单纯地依赖于任何特定均衡算法
(bancing algorithm),我觉得是不可能的。
即使是一致性哈希(consistent hashing),在集群增加或移除节点时,也终究会导致部分节点被重定向,这样下一次的请求会发送到不同于原来节点的新节点上去,这样原来的节点上的缓存信息也就失效了。
假设我们硬是要用一致性哈希的话,那要应对缓存失效可能就需要一个app server将session缓存数据转移到其他app server上去,而这对app server又做出了新要求,即耦合了app server的代码。显然,对于不同的app server来说这就是个折磨,相当于自己做类似分片管理的工作。要运用一致性哈希的系统,我觉得应该是分布式web缓存(即最初的用意)或是内存型数据库(比如amazon的dynamo)。
当然,由于我对web不是太懂,以上的对于永不过期的session来说确实会造成一定的影响。但是实际上是,session信息作为一种缓存,通常也会设置其有效期,因此即使出现部分缓存失效,是可以接受的。
后端缓存
除此之外,我们还能考虑在后端数据库的“后端”(套娃)弄一个内存缓存
(e.g. redis,memcached),这样使app server成为无状态的
(stateless)。除此之外,由于缓存与服务器分离,因此服务器即使崩溃重启(如果有replication,往往是比较少见的)或者集群变化等均不会引起session丢失。从这点来说,该方案可能会适合做横向扩展。
路由缓存
如果对于会话持久十分严格,还可以在LB层缓存路由信息,这样即使集群变化,路由表不变的话,亦然可以路由到原来的服务器。
如果空间足够的话,考虑本机缓存即可;不然,还是弄个分离的缓存服务器比较好。
那么路由表中的数据是不变的吗?这个我认为是需要进行清理的,比如很久之前的路由信息很明显可以清除。换言之,需要支持限制缓存大小和设置过期时间,那么采用支持这类功能的内存缓存比较好,比如redis。
另外,还有一种可能的方案,就是采用Session Cookie,感兴趣可以看下[1]
总结
一致性哈希(session存在有效期且部分重定向是可接受的)
- Pros:
- 不需要调整app server代码
- 重定向的节点只有两个节点(新节点和指针方向的下一个节点)
- Cons:
- 弱会话持久性,不能保证重定向的请求已过期
- 实现一致性哈希,比如增设虚拟节点,是比较tricky的工作
- Pros:
后端缓存
- Pros:
- app server不需要缓存和维护状态
- 将缓存与app server分离,使app server不受集群影响
- 缓存系统可能支持集群扩容等功能,某种程度上增强了灵活性
- Cons:
- app server代码需要进行一定的调整
- 多一次中间层开销(包括网络IO开销和缓存本身处理请求的开销)
- Pros:
路由缓存
- Pros:
- 强会话持久性,即使采用不确定性均衡算法
- 原来的节点路由不受集群影响
- 不需要调整app server代码
- Cons:
- 在LB层付出开销,可能影响路由性能。具体来说,如果是本地存储元数据,那么可能占用过多的空间;反之,如果是采用分离的内存缓存,那么需要多一次中间层开销。
- Pros:
reference
load-balancing-affinity-persistence-sticky-sessions-what-you-need-to-know
分布式session解决方案与一致性hash