原文:http://blog.jobbole.com/83475/
一、简介
因为大多数网站在用户使用时候都是要保持用户的状态,具体点就是网站要记住请求是归属到那一个客户端,而这个状态在网站开发里就是通过会话session来体现的。分开部署的web应用服务要解决的一个首要问题就是要保持不同物理部署服务器之间的session同步问题,从而达到当用户第一次请求访问到服务器A,第二个请求访问到服务器B,网站任然知道这两个请求是同一个人,解决方案很直接:服务器A和服务器B上的session信息要时刻保持同步,那么如何保证两台服务器之间session信息的同步呢?
为了回答上面的问题,我们首先要理解下session的机制,session信息在web容器里都是存储在内存里的,web容器会给每个连接它的客户端生成一个sessionid值,这个sessionid值会被web容器置于http协议里的cookie域下,当响应被客户端处理后,客户端本地会存储这个sessionid值,用户以后的每个请求都会让这个sessionid值随cookie一起传递到服务器,服务器通过sessionid找到内存中存储的该用户的session内容,session在内存的数据结构是一个map的格式。
二、解决方案
方案一、服务器之间拷贝和同步
为了保证不同服务器之间的session共享,那么最直接的方案就是让服务器之间session不断的传递和复制,例如java开发里常用的tomcat容器就采用这种方案,以前我测试过tomcat这种session同步的性能,我发现当需要同步的web容器越多,web应用所能承载的并发数并没有因为服务器的增加而线性提升,当服务器数量达到一个临界值后,整个web应用的并发数甚至还会下降,为什么会这样了?
原因很简单,不同服务器之间session的传递和复制会消耗服务器本身的系统资源,当服务器数量越大,消耗的资源越多,当用户请求越频繁,系统消耗资源也会越来越大。如果我们多部署服务器的目的只是想保证系统的稳定性,采用这种方案还是不错的,不过web应用最好部署少点,这样才不会影响到web应用的性能问题,如果我们还想提升网站的并发量那么就得采取其他的方案了。
方案二、使用独立缓存服务器存储
时下使用的比较多的方案就是使用独立的缓存服务器,也就是将session的数据存储在一台独立的服务器上,如果觉得存在一台服务器不安全,那么可以使用memcached这样的分布式缓存服务器进行存储,这样既可以满足了网站稳定性问题也提升了网站的并发能力。
但是即使采用分布式缓存服务器例如memcached,如果缓存服务器集群中一台服务器挂掉,也会造成其存储的session的丢失。假如我们使用两台服务器做缓存服务器来存储session信息,那么如果其中一台服务器挂掉了,那么网站将会有一半的用户将不能正常使用网站,原因是他们的session信息丢失了,网站无法正常的跟踪用户的会话状态。
之所以提到这个问题是想告诉大家以memcached为代表的分布式缓存和我们传统理解的分布式系统是有区别的,传统的分布式系统都会包含一个容灾维护系统稳定性的功能,但实际的分布式技术是多种多样的,例如memcached的分布式技术并不是为了解决容灾维护系统稳定性的模式设计,换个说法就是memcached集群的设计是没有过分考虑冗余的问题,而只有适当的冗余才能保证系统的健壮性问题。分布式技术的实现是千差万别的,每个优秀的分布式系统都有自身独有的特点。
这里我简单的介绍下memcached实现的原理,当网站使用缓存集群时候,缓存数据是通过一定的算法将缓存数据尽量均匀分不到不同服务器上,如果用户A的缓存在服务器A上,那么服务器B上是没有该用户的缓存数据,早期的memcache数据分布式的算法是根据缓存数据的key即键值计算出一个hash值,这个hash值再除以缓存服务器的个数,得到的余数会对应某一台服务器,例如1对应服务器A,2对应服务器B,那么余数是1的key值缓存就会存储在服务器A上,这样的算法会导致某一台服务器挂掉,那么网站损失的缓存数据的占比就会比较高。为了解决这个问题,memcached引入了一致性hash算法。关于此请参考《memcache的一致性hash算法使用》
方案三、客户端存储
不过早期的淘宝在这个问题解决更加巧妙,他们将session的信息直接存储到浏览器的cookie里,每次请求cookie信息都会随着http一起传递到web服务器,这样就避免了web服务器之间session信息同步的问题,这种方案会让很多人诟病,诟病的原因是cookie的不安全性是总所周知的,如果有人恶意截取cookie信息那么网站不就不安全了吗?这个答案还真不好说,但是我觉得我们仅仅是跟踪用户的状态,把session存在cookie里其实也没什么大不了的。
方案四、session粘滞
在我的公司里客户端的请求到达web服务器之前,会先到F5,F5是一个用来做负载均衡的硬件设备,它的作用是将用户请求均匀的分发到后台的服务器集群,F5是硬件的负载均衡解决方案,如果我们没那么多钱买这样的设备,也有软件的负载均衡解决方案,这个方案就是大名鼎鼎的LVS了,这些负载均衡设备除了可以分发请求外它们还有个能力,这个能力是根据http协议的特点设计的,一个http请求从客户端到达最终的存储服务器之前可能会经过很多不同的设备,如果我们把一个请求比作高速公路上的一辆汽车,这些设备也可以叫做这些节点就是高速路上的收费站,这些收费站都能根据自己的需求改变http报文的内容,所以负载均衡设备可以记住每个sessionid值对应的后台服务器,当一个带有sessionid值的请求通过负载均衡设备时候,负载均衡设备会根据该sessionid值直接找到指定的web服务器,这种做法有个专有名词就是session粘滞,这种做法也比那种session信息在不同服务器之间拷贝复制要高效,不过该做法还是比存cookie的效率低下,而且对于网站的稳定性也有一定影响即如果某台服务器挂掉了,那么连接到该服务器的用户的会话都会失效。
三、总结
解决session的问题的本质也就是解决session的存储问题,其本质也就是解决网站的存储问题,一个初建的网站在早期的运营期需要解决的问题基本都是由存储导致的。上文里提到时下很多新建的web应用会将服务器部署后云平台里,好的云平台里或许会帮助我们解决负载均衡和session同步的问题,但是云平台里有个问题很难解决那就是数据库的存储问题,如果我们使用的云平台发生了重大事故,导致云平台存储的数据丢失,这种会不会导致我们在云平台里数据库的信息也会丢失了,虽然这个事情的概率不高,但是发生这种事情的几率还是有的,虽然很多云平台都声称自己多么可靠,但是真实可靠性有多高不是局中人还真不清楚哦,因此使用云平台我们首要考虑的就是要做好数据备份,假如真发生了数据丢失,对于一个快速成长的网站而言可能非常致命。