实际的生产环境如下所示,为了保证高可用性,所有的服务器包括应用服务器\WebServer\WebSeal都做了负载均衡,最前端是由一台F5负载均衡交换机进行分发。应用服务器和IHS实际有两台机器,都分别部署了WAS和IHS,每个WAS都创建了两个profile,每两个profile做一个集群,两个个集群上分别部署了不同的应用(portal和OA)。
方便起见,两个Cluster使用了同一个DM进行管理,在这个DM里面,又注册进来两个IHS,并且两个IHS都做相同配置,每个IHS都可以对两个cluster进行负载均衡转发。这样的效果是,只需要访问呢http://ihs1/oa就能访问OA了,访问http://ihs1/portal就能访问portal了,主机名换成ihs2也是一样,其中任何一个服务器down掉也可以继续提供服务。
webserver貌似把后端各应用服务器的地址、端口都隐藏起来了,只需要通过一个地址和端口,通过不同的上下文,就可以访问不同应用服务器上应用了。
恩,看上去很爽,但有什么问题呢?为方便起见,我们把这个部署简化一下,去掉所有的集群相关的东西,F5和webseal都不要了,只说最关键的东西。
简化后的部署如下图,只剩下一个IHS,后端两台应用服务器,分别部署portal和OA。实现的效果仍然是只需要通过http://ihs/oa和http://ihs/portal就可以分别访问portal和OA了。
这样的部署,如果OA和Portal之间各管各的,不需要在一个浏览器进程中同时访问两个应用,也没有任何问题。但如果两个应用间确实有联系,比如需要先访问portal,然后链接到一个OA的页面,操作完了再返回portal页面。而且这些操作都是要先登录认证的,而且portal和OA都是使用session来维护当前用户信息的,问题就来了。你可能会发现,访问完OA后,再去访问portal,portal又要你登录了!
真是奇怪呀,这是为什么呢?稍微分析一下,其实也很简单。
我们都知道,http协议是不能保持状态的,所有应用服务器会使用一个session cookie来识别当前用户维护状态。在一般的java应用里,这个cookie的名字叫jsessionid。当应用第一次请求访问session时,应用服务器就会为这个请求生成一个session,并将相应seesionid通过这个cookie发送到客户端。在以后(同一个浏览器进程)的每次访问请求中,浏览器都会将这个cookie又传回到应用服务器,通过这个sessionid应用服务器又能为当前用户获取到session了,是不是很简单?
在我们的场景中,用户现在appsrv1进行了一次登录,appsrv1生成了一个session通过jsessionid这个cookie名称发送给浏览器,因为有个ihs做分发,所以浏览器认为这个cookie是ihs设置的。当用户访问appsrv2时,浏览器仍然是面对的ihs,所以仍将jseesionid发送了过去。appsrv2拿到这个jsessionid,到自己维护的session列表中一查询,肯定没有这个id了,所以认为是无效的,appsrv2自己又生成一个session,并将新的sessionid发送到客户端。客户端再访问appsrv1,这时候sessionid已经变成appsrv2生成的了,在appsrv1中肯定又找不到了,当然就会再要求你重新登录了~~~~
解决这个问题,比较容易想到的当然是改一下每个应用服务器用来维护session的cookie名称,不要都使用默认的jessionid,这样各管各的,当然就没问题了,但这个要应用服务器支持才行。要么就使用多个ihs,每个集群都使用专用的ihs,也OK。在我们的生产环境中,因为使用了webseal反向代理,我的方法是分别为两个集群的上下文配置了一个junction,webseal就会分别去维护两个集群的cookie了,相当与是用了两个浏览器在同时访问应用,也就不会有session冲突的问题了。