Archive

Archive for the ‘Java’ Category

WebSphere JSession IDs

March 5th, 2012 tonyxu No comments

转自:https://www.ibm.com/developerworks/mydeveloperworks/blogs/Dougclectica/entry/websphere_session_ids22?lang=zh

During investigation of some intermittent problems in one of our web applications, we observed some JSESSIONID cookie behavior that we couldn’t explain, so I performed some investigation into the cookie’s mechanics. I’ll attempt to summarize in this article what I learned.

JSESSIONID Cookie Format

In a clustered environment, the JSESSIONID cookie is composed of the core Session ID and a few other components. Here’s an example:

JSESSIONID=0000A2MB4IJozU_VM8IffsMNfdR:v544d0o0

Component

Example value

Cache ID 0000

Session ID A2MB4IJozU_VM8IffsMNfdR

Separator :

Clone ID or Partition ID v544d0o0

Cache ID

It’s still unclear to me what the different values mean here, but searching our Proxy logs indicates that the vast majority of our Sessions, the Cache ID is 0001. For instance, a search of one day’s log around 17:00 indicates the following number of cookies which contained a particular Cache ID:

Cache ID   Hit count

0000   4479

0001    223662

0002   191

For a particular Session ID, the Cache ID can definitely change mid-session, without any other changes. In particular, without the Clone/Partition ID changing. This does not indicate a switch to another Cluster member, but I don’t know exactly what it does indicate. Based on the relative loads of our various systems, it seems possible that changing Cache IDs only occurs under heavy loads.

Clone/Partition ID

A Partition ID is appended to the cookie if memory-to-memory replication in peer-to-peer mode is utilized for Distributed Session management. Otherwise, a Clone ID is appended.

Clone ID

This will match one of the CloneID attributes in the Server elements within the web server’s plugin-cfg.xml file. For instance 138888kcd in:

<Server CloneID="138888kcd" ConnectTimeout="10" ExtendedHandshake="false"        LoadBalanceWeight="2" MaxConnections="-1" Name="server1Node01_App03"        ServerIOTimeout="0" WaitForContinue="false">...</Server>
Partition ID

If you use memory-to-memory Session replication, your cookies will contain Partition IDs rather than Clone IDs.

Partition IDs are similar in function to Clone IDs, but best I can tell there is no direct way to determine which values correspond to which cluster members. They are internally mapped by the WebSphere HAManager to specific Clone IDs, and that mapping is exchanged with the web server plug-in so that it can maintain Session affinity and find additional cluster members for failover. (The exchange takes place in private headers on each response from WAS back to the plug-in, and those headers are removed before the response is sent back to the client.)

Session Affinity and Failover

The Clone/Partition ID corresponds to whichever cluster member creates the Session, and the plug-in is responsible to send that Session to the same cluster member as long as it is available. From the Scalability Redbook:

Since the Servlet 2.3 specification, as implemented by WebSphere Application Server V5.0 and higher, only a single cluster member may control/access a given Session at a time. After a Session has been created, all following requests need to go to the same application server that created the Session. This Session affinity is provided by the plug-in.

If on a subsequent request the specified cluster member is unavailable, the plug-in will choose another cluster member and attempt to connect to that. If Distributed Sessions are configured, via database persistence or memory-to-memory replication, the Session will be resumed in-progress on that new member. If not, a new Session will be created and the user’s progress will be lost.

If a new cluster member is able to resume the existing Session, it will append its own Clone/Partition ID to the existing JSESSIONID cookie. For instance:

JSESSIONID=0000A2MB4IJozU_VM8IffsMNfdR:v544d0o0:v544d031

Now the plug-in knows that 2 different cluster members could potentially service this Session. If the original member becomes available again, the Session will switch back to it.

Finally, note that according to the System Management Redbook:

WebSphere provides session affinity on a best-effort basis. There are narrow windows where session affinity fails. These windows are:

  • When a cluster member is recovering from a crash, a window exists where concurrent requests for the same session could end up in different cluster members…
  • A server overload can cause requests belonging to the same session to go to different cluster members…
Categories: Java, WAS Tags: ,

转:jsessionid传递方式对session实现机制

January 25th, 2011 tonyxu No comments

转自:http://www.java125.cn/article.asp?id=2402

apache + resin的多机负载分布和多个webapp统一认证的实现方案, 期间设计多个webapp统一认证的实现方案时, 发现resin下通过cookie来传递jsessionid和通过url重写将jsessionid放url中传递, 会有细微的差异.
在servlet规范中,HttpServletSession的获取时通过调用request.getSession(boolean createnew)方法来实现,其实现机制可以简单的理解为: 存在一个大的hashMap结构,key就是jsessionid,而valule是HttpservletSession对象。 request.getSession(boolean createnew)方法通过jsessionid来获取对应的HttpservletSession,如果不存在并且参数createnew= true,则创建一个新的HttpservletSession对象,并设置jsessionid=session.getId() ,保存到hashMap结构中。以后再传递这个jsessionid. (详细的过程比较复杂,各家的实现也不尽相同,但大体的实现原理是如此。)
关注以下几点:
一). 获取jsessionid
    jsessionid的传递可以是以下途径
    1. 放在cookie中
        Cookie: JSESSIONID=abcrmF3Gx-5Z-hhkgHfzr
    2. 以参数形式放在url
http://10.3.2.35:11280/wmail/welcome.action?jsessionid=abcQNqiT4C01rg-necLBr
    3. 用form表单传递,通常是用隐藏域
        <input type="hidden" name="jsessionid" value="abcQNqiT4C01rg-necLBr"/>
    4. url重写
http://10.3.2.35:11280/jid=abcQNqiT4C01rg-necLBr/wmail/welcome.action
           或者
http://10.3.2.35:11280/wmail/welcome.action;jsessionid=abcQNqiT4C01rg-necLBr
    如果当前还没有jsessionid则当然就无法获取,通常用户第一次访问或者登录前就是这种情况.
    可以通过request.getRequestedSessionId() 方法来获取本次http 请求的jsessonid值。
二)获取到的HttpServletSession对象
    如果HttpServletSession对象是已经存在的,则
    1. session.isNew()=false
    2. request.getRequestedSessionId() == jsessionid == session.getId()
    如果HttpServletSession对象是调用request.getSession(true) (简写的request.getSession()方法等同于request.getSession(true) )时新创建的,则有以下特征:
    1. session.isNew()=true
    2. 以后传递的jsessionid=session.getId()
        注意这里,如果request.getRequestedSessionId() 是空值,情况比较简单,以后传递jsessionid=session.getId()就是了。
        但是如果request.getRequestedSessionId() 不是空值,通过这个值没有获取到已经存在的session对象,而是返回了一个新的session对象,这个时候新的session.getId()和原有的request.getRequestedSessionId() 关系如何呢?下面详细阐述这种情况。
三) request.getRequestedSessionId() 不是空值时,新的session.getId() = ?
    1). 测试代码如下:
    HttpServletRequest request = ServletActionContext.getRequest();
    String jid1 = request.getRequestedSessionId();
    HttpSession session = request.getSession(true);
    String jid2 = request.getRequestedSessionId();
    logger.info("get HttpSession , isNew()=" + session.isNew()
                    + " getId()=" + session.getId()
                    + " and jid1=" + jid1
                    + " and jid2=" + jid2);
    其中jid1和jid2分别是调用request.getSession(true)方法前后的request.getRequestedSessionId()值。
    在resin中运行以上代码,测试request.getRequestedSessionId() 不是空值而对应jsessionid的session不存在的情况。
    2). 通过cookie来传递jsessionid的情况,测试结果如下:
        get HttpSession, isNew()=true getId()=abcqIgQroQ2Ov9lGYcYAr and jid1=abcqIgQroQ2Ov9lGYcYAr and jid2=abcqIgQroQ2Ov9lGYcYAr
        get HttpSession, isNew()=true getId()=abcPQ3mpxKz8H-4UMdYAr and jid1=abcPQ3mpxKz8H-4UMdYAr and jid2=abcPQ3mpxKz8H-4UMdYAr
        get HttpSession, isNew()=true getId()=abcdeE3iDy_bI536tLYAr and jid1=abcdeE3iDy_bI536tLYAr and jid2=abcdeE3iDy_bI536tLYAr
        可以发现以下规律:
        1.  isNew()=true
        2. session.getId() == jid1 == jid2
            即新创建的session会使用传递过来的jsessionid值,即使这个jsessionid值根本没有对应的session存在
    3)  通过url重写,将jsessionid放url中传递, 测试结果如下:
        get HttpSession, isNew()=true getId()=abccw1zEC_RcN43qHMYAr and jid1=abcdUdTfKuLbge8h_LYAr and jid2=abcdUdTfKuLbge8h_LYAr
http://10.3.2.35:11280/jid=abccw1zEC_RcN43qHMYAr/uab/contactList.action
        get HttpSession, isNew()=true getId()=abcFK7yOB1irgaYqgNYAr and jid1=abci-HpMPJU3egCB7MYAr and jid2=abci-HpMPJU3egCB7MYAr
http://10.3.2.35:11280/jid=abcFK7yOB1irgaYqgNYAr/uab/contactList.action
        (后面的http地址为页面跳转完成后显示在浏览器地址框中的页面url)
        可以发现以下规律:
        1. isNew()=true
        2. jid1 == jid2
            request.getRequestedSessionId()值在request.getSession(true)方法调用前后无变化
        3. session.getId()  != jid1
            即新创建的session不使用传递过来的jsessionid值,而是采用新值
        4. 跳转完成后的http地址中,使用的是session.getId(), 而不是原来通过url重写传递过来的jsessionid
            此时新的jsessionid覆盖了旧有的jsessionid.
    4) 总结
        在resin的实现中, 通过cookie来传递jsessionid的情况和通过url重写将jsessionid放url中传递, 会有细微的差异.
        以上测试的resin版本为3.0.26, 稍后有时间考虑测试其他版本和tomcat.
        这个差异直接影响到跨webapp的多个webapp直接相互传递jsessionid的方式, 通过cookie传递jsessionid可以做到多个webapp之间在页面跳转时始终是一个相同的jsessionid,这种各个应用都可以方便的获取到自己的HttpServletSession对象. 但是如果是通过url重写,则破坏了jsessonid的一致性, 逼迫各个webapp之间跳转时必须用其他额外的方法来保证传递给对方的jsessionid的准确性,因为此时每个webapp的jsessionid 都不一样了,必须记住其他每个webapp的jsessionid,造成跨webapp的页面跳转极其复杂,难于接受

补充:

cookie和session机制区别与联系

   具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上它还有其他选择。

    cookie机制。正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie。而cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置,则把该cookie附在请求资源的HTTP请求头上发送给服务器。
    cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。若不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。若设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式
    session机制。session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。
    当程序需要为某个客户端的请求创建一个session时,服务器首先检查这个客户端的请求里是否已包含了一个session标识(称为session id),如果已包含则说明以前已经为此客户端创建过session,服务器就按照session id把这个session检索出来使用(检索不到,会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个session id将被在本次响应中返回给客户端保存。
    保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于SEEESIONID。但cookie可以被人为的禁止,则必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。
    经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面。还有一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把session id传递回服务器。比如:
     <form name="testform" action="/xxx">
     <input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764">
     <input type="text">
     </form>
实际上这种技术可以简单的用对action应用URL重写来代替。