写在前面
最近在读一本来自淘宝技术团队大牛的书,名字叫《大型网站系统与Java中间件实践》。开篇的章节详细地介绍了一个网站架构由小变大不断演进的过程,其中从单机架构升级到集群架构的过程中着重介绍了关于session同步问题, 这也是很多人在聊到分布式时绕不过去的话题。下面就整理下书中的内容,也算是做个读书笔记,方便以后参考。
问题从哪来
做web开发的同学应该对session再熟悉不过,它是服务器分配给客户端的会话标识,浏览器每次请求会带上这个标识来告诉服务器我是谁,服务器会在内存中存储这些不同的会话信息,由此来分辨请求来自哪个会话。在单机部署的环境总,因为web服务器和session都是在同一台机器上,所以必然能找到对应的会话数据。但如果有2台web服务器(A和B)提供服务,假如第一次请求落到A上并创建了session,那么如何保证下次落到B的请求能读到session数据?
解决方案
有以下4中常见的解决方案。
1、Session Sticky
这是最简单粗暴的 方法,核心思路就是让同一会话的请求都落地到同一台服务器上,这样处理起来就和单机一样了,我们可以在负载均衡上做一些身份识别并控制转发来达到这个目的。这样做的优势是能像单机一样简化对session处理,也方便做本地缓存,但缺点也是很明显的:
- 如果这台服务器宕机或重启了,那么所以的会话数据都会丢失,失去了分布式集群带来的高可用特性。
- 增加了负载均衡器的负担,使它变得有状态了,而且资源消耗会更大,容易成为性能瓶颈。
2、Session Replication
顾名思义,这是一种session复制的方案,核心思路就是通过在服务器之间增加session同步机制来保证数据一致。
看起来比第一种简单了很多,也没有第一种带来的缺陷,但在某些应用场景下还是会有比较严重的问题:
- 服务器之间的数据同步带来了额外的网络消耗,随着机器数量和数据量的上升,网络带宽将会有很大的压力,也必然会带来延时问题。
- 每台服务器上都要存储所有的会话数据,如果会话数量很大会占用服务器大部分内存空间。
目前很多应用容器都支持这种同步方式,所以在集群规模和数据量比较小的时候还是一种很好的解决方案。
3、Session集中存储
这种方式的思路就是把所有的会话数据统一存储和管理,所有应用服务器需要对session进行读写都要通过session服务器来操作:
这种方案的好处是独立了session的管理,职责单一化,session服务器采用什么方式存储(内存、数据库、文档、NoSql等等),什么方式对外提供服务都是透明的。不会给应用系统和负载均衡带来额外的开销,不需要进行数据同步就能保证一致性,看起来应该是非常完美了,不过也有自己的一些小缺陷:
- 对session读写需要网络操作,相比较session直接存储在web服务器的时候增加了时延和不稳定性,好在session服务器和web服务器一般是部署在局域网中,可以最大化减少这个问题。
- session服务器出现问题将影响所有web服务,如果采用多机部署同时也会带来数据一致性问题。
每种方案带有它独特的优势,同时也会带来相应的新问题,正所谓没有十全十美,只有适合才是最好的。总体来说,这种方案在应用服务器和会话数据量都很大的时候还是非常有优势的。
4、Cookie Base
这种方案是基于cookie的传输来实现的,核心思想很简单,就是把完整的会话数据经过处理后写入到客户端cookie,以后客户端每次请求都带上这个cookie,然后服务端通过解析cookie数据来获取会话信息,如下图所示:
这种方案简单明了,也没有前面几种方案带来的问题,但劣势也非常明显:
- 首先通过cookie来传递关键数据肯定是不安全的,即便是采用了特殊的加密手段。
- 如果客户端禁用了cookie,将直接导致服务不可用。
- cookie的数据是有大小限制的,如果传递的数据超出限制大小,将会导致数据异常。
- 在http请求中携带大量的数据进行传输会增加网络负担,同样,服务端响应大量数据会导致请求变慢,并发量大的时候会非常可怕。
总结
以上4种方案都是可行的方案,正如前面所说,每种方案各有优劣,不会十全十美,实际应用中要根据需求做权衡和取舍。这些都是属于比较通用的方案,我相信在真正的实践和落地过程中还会有其他问题出现,有经验的过来人或许会有一些另辟蹊径的“套路”,欢迎讨论交流。