传统的B/S架构的系统中,一般为单点部署,并不存在集群,所以也不存在session丢失的问题。那么,由于单点部署一旦宕机,无法保证系统可用性,那我们就想到把它扩展为多台服务器部署,这样既保证了系统的可用性,又降低了单点的压力。但是,在集群环境下,原来的application级别的session,由于只局限于单点容器中,并不能保证可见,所以必然会造成用户会话状态的丢失。如下图:
cookie存储这种方式很简单,因为cookie特点是在server端生成,保存在client端。这样我们就可以,在创建cookie的时候,把user的相关信息存储到cookie里面。因为cookie默认生命周期就是关闭浏览器即消失。另外,cookie是通过http header传输的,我们可以在server端拦截,根据cookie的name获取user的信息。
缺点:
1,cookie在浏览器中存储,存在安全隐患。
2,cookie存储数据大小为4K,浏览器最多保存300个cookie限制。
3,每一次请求都会带着cookie,cookie中值过大会增加网络开销。
结论:内网系统可以使用,互联网中不推荐。
其原理就是,Tomcat服务器会把自己存储的session广播同步给集群中其他的节点。没什么难点,给出配置如下:
缺点:1,首先session广播是要通过网络在集群中的各个Tomcat节点上传输,本身Tomcat就是要处理外部请求,这样占用了服务的带宽资源,会使Tomcat处理能力下降。
2,传输过后,各节点也要有replication,所以对磁盘IO也增加了压力。
3,由于有网络传输和节点sync,所以很有可能在下一次请求到来时,没有做完这两件事,直接导致找不到session。
结论:节点不多时,可以作为临时方案,但不推荐。
ip hash,顾名思义是根据请求client端的ip做hash运算再对服务组取模运算,得到的就是routeNo。现有的web服务器有支持这种路由算法,如nginx,apache+JK(类似,但它其实为粘性session Sticky)等。
eg:
upstream backserver {
ip_hash;
server 192.168.0.14:88;
server 192.168.0.15:80;
}
缺点:
1,这种负载均衡策略,并不能很均衡的在集群中分配流量,导致流量可能会集中在某一台服务器上面。
2,一旦出现某一节点宕机的情况,那这个节点的会话就会丢失。
结论:流量不是特别大的项目,可以用。
缓存共享的方式,是通过外部的缓存或存储器去解决session对于各个application的可见性,统一性。一般,考虑性能,稳定性方面,会选择Redis或memcache来保存user信息,根据相应的规则生成一串唯一的安全的摘要串作为KEY。而在这个KEY也叫token或被种到cookie里面,请求会带着这个token来保持会话的状态。
这里多说一嘴,其中tomcat 8.x开始新增了相应的标签空间,可以直接集成memcache了。
eg:
failoverNodes="n2"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory"/>
缺点:
1,压力在于你的缓存服务,因为所有的用户信息都会被缓存在你的Redis里。
2,系统有一定复杂性。
结论:可用性,扩展性都不错,推荐。
其他对于session共享的解决方案,也有,例如:商业版的session存储盒子(可以把它看成一个存储器,独立部署一个服务器上面。),使用硬件F5做粘性会话,成本太高,后期也不好维护等等。其他方式不是特别合适,所以不做一一介绍。
单点登录简称为 SSO。其意思就是,在一个系统或者网站中登录以后,再点击跳转到另一个或者多个系统或者网站,可以直接获取到用户的会话信息,不用再重复登录。
单点登录在分布式系统架构中,都是单独分离出来,作为独立项目和服务进行部署和维护的。可以提供相应的RPC协议,也可以提供HTTP协议。
单点登录主要的难点有两点:1,session的共享(我们上面已经讨论过)。2,cookie的跨域。
我们下面来看看什么是cookie跨域,怎么样才能cookie跨域。
因为cookie是在服务端生成的,保存在浏览器端,只有浏览器在访问相应的域名(在服务端种cookie时,绑定的域名),所以你的cookie就只能在当前域名下有效。
一般好多公司都会使用子域名,这样就可以把cookie种到主域名下,那各个的子域名下都会取到这个cookie。
当然,还有那种,域名完全不相同的情况,解决的方案也有好多种。比如抛弃cookie,转为URL参数加token,乐视云计算官网以前就是这样解决的。再比如在当前页面用js创建一个iframe,把当前域A的cookie作为参数,用iframe的属性src重定向的方式调用B域的接口,B域拿到A的cookie再种下来。再比如也可利用jsonp($getJson)强行跨域,后端在相应处理。还有耶鲁大学的开源框架cas,支持mysql,redis等,也集成了shiro,但是对调用方的侵入比较严重,设计还可以。
综上,解决集群分布式环境的会话问题,要充分的了解session和cookie的机制,还要做好分布式场景中的系统界限划分。