posts - 42,comments - 83,trackbacks - 0
        在weblogic中,sesion persitence有如下几种方式:
    memory—Disables persistent session storage.
    file—Uses file-based persistence (See also PersistentStoreDir, above).
    jdbc—Uses a database to store persistent sessions. (see also PersistentStorePool, above).
    replicated—Same as memory, but session data is replicated across the clustered servers.
        cookie—All session data is stored in a cookie in the user's browser.
        replicated_if_clustered—If the Web application is deployed on a clustered server, the in-effect PersistentStoreType will be replicated. Otherwise, memory is the default.

        本文讨论的重点是持久化导致的性能问题,故只讨论JDBC和File两种方式,其余的不做讨论,Session相关的Params,请参考如下链接,
        http://e-docs.bea.com/wls/docs81/webapp/weblogic_xml.html#1038173

        首先说一下File persistence, 要使http session被写入到指定f目录下的file中,需要在/WEB-INF/weblogic.xml做如下配置:

weblogic.xml <session-param>
        <param-name>PersistentStoreType</param-name>
        <param-value>file</param-value>
      </session-param>
      <session-param>
        <param-name>PersistentStoreDir</param-name>
        <param-value>d:\session_test</param-value>
      </session-param>
      <session-param>
        <param-name>CacheSize</param-name>
        <param-value>10</param-value>
      </session-param>
     </session-descriptor>

        测试我们使用weblogic自带的mainWebApp, 该web app位于类似如下的位置
        D:\beasys\wls816\weblogic81\samples\server\examples\build
        测试前,按上面的写法修改weblogic.xml, 然后修改index.jsp, 去掉头部的如下内容:
<%@ page session="false" %>
 并修改其内容如下:
 1 <%@ page import="java.util.ArrayList" %>
 2 
 3 <%
 4   String url = "http://" + request.getServerName() + ":" + request.getServerPort();
 5   session.setAttribute("key""value");
 6   ArrayList list = new ArrayList();
 7     for(int loop = 0; loop < 100000; loop++)
 8     {
 9             list.add("test" + loop);
10     }
11     session.setAttribute("list", list);
12 %>

        上面的代码中,我往每个session里插入12M左右的数据,结果跟客户说的一样,这种性能会死人的,如果做4个并发的话,差不多需要5分钟以上。就这性能,我反正是快崩溃了,做了一下thread dump

Title

"ExecuteThread: '10' for queue: 'weblogic.kernel.Default'" daemon prio=5 tid=0x032d9208 nid=0xd78 waiting for monitor entry [0x03a2f000..0x03a2fd68]
 at weblogic.servlet.internal.session.FileSessionContext.loadSession(FileSessionContext.java:482)
 - waiting to lock <0x159a82a8> (a java.lang.Object)
 at weblogic.servlet.internal.session.FileSessionContext.getSessionInternal(FileSessionContext.java:626)
 at weblogic.servlet.internal.ServletRequestImpl.getValidSession(ServletRequestImpl.java:2639)
 at weblogic.servlet.internal.ServletRequestImpl.getSession(ServletRequestImpl.java:2251)
 - locked <0x29b68f58> (a java.lang.Object)
 at weblogic.servlet.internal.WebAppServletContext.invokeServlet(WebAppServletContext.java:3752)
 at weblogic.servlet.internal.ServletRequestImpl.execute(ServletRequestImpl.java:2773)
 at weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:224)
 at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:183)

"ExecuteThread: '9' for queue: 'weblogic.kernel.Default'" daemon prio=5 tid=0x032d85e0 nid=0x10c0 waiting for monitor entry [0x039ef000..0x039efd68]
 at weblogic.servlet.internal.session.FileSessionContext.loadSession(FileSessionContext.java:482)
 - waiting to lock <0x159a82a8> (a java.lang.Object)
 at weblogic.servlet.internal.session.FileSessionContext.getSessionInternal(FileSessionContext.java:626)
 at weblogic.servlet.internal.ServletRequestImpl.getValidSession(ServletRequestImpl.java:2639)
 at weblogic.servlet.internal.ServletRequestImpl.getSession(ServletRequestImpl.java:2251)
 - locked <0x13d804f0> (a java.lang.Object)
 at weblogic.servlet.internal.WebAppServletContext.invokeServlet(WebAppServletContext.java:3752)
 at weblogic.servlet.internal.ServletRequestImpl.execute(ServletRequestImpl.java:2773)
 at weblogic.kernel.ExecuteThread.execute(ExecuteThread.java:224)
 at weblogic.kernel.ExecuteThread.run(ExecuteThread.java:183)

"ExecuteThread: '8' for queue: 'weblogic.kernel.Default'" daemon prio=5 tid=0x032d79b8 nid=0x12cc runnable [0x039af000..0x039afd68]
 at java.io.FileOutputStream.write(Native Method)
 at java.io.DataOutputStream.write(DataOutputStream.java:66)
 - locked <0x10b2bf28> (a java.io.DataOutputStream)
 at java.io.FilterOutputStream.write(FilterOutputStream.java:60)
 ......
 at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1052)
 at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:278)
 at weblogic.servlet.internal.session.FileSessionContext.sync(FileSessionContext.java:368)
 - locked <0x159a82a8> (a java.lang.Object)
 - locked <0x1f9cab60> (a weblogic.servlet.internal.session.FileSessionData)


         看了Thread dump, 发现居然很多线程在等同一lock,如上面的0x159a82a8,而这个lock的holder居然是FileSessionContext.java。看了一下代码,感觉这代码太太那啥了,

FileSessionContext.sync(HttpSession sess)
 1         saveTo = getSessionPath(id);
 2         synchronized (dirTreeLock) {
 3           makeStorageDir(id);
 4           os = new DataOutputStream(new FileOutputStream(saveTo));
 5           WLObjectOutputStream oos = new WLObjectOutputStream(os);
 6           oos.setReplacer(RemoteObjectReplacer.getReplacer());
 7           oos.writeObject(data);
 8           oos.flush();
 9           os.writeLong(data.getLastAccessedTime());
10           oos.close();
11           os.close();
12         }

        有了这样的代码,这么差的性能就不足为奇了。同样的代码还出现在loadSession()中。这些原本应该由FileSessionData(对应于每一个具体的Session)完成的工作,都交给了FileSessionContext去做。SessionContext是什么,顾名思义,Session的上下文,Session的管理者,同一个WebApp的所有Session都由它管理。原本是个管理者,这里却沦落为工兵。打个比方,老板手下50小兵,每个小兵每天要写100行代码,结果小兵都不干活,都交给老板去做了,老板一天要写5000行,而且必须串行完成,不带并行干活的。这样的老板谁干,不累死才怪。

        开始别人说file persitence性能差,我不加思索的来了个结论:不差才怪,一个文件,只能串行写入,能有什么好的性能? 后来自己做测试的时候才发现,压根不是我想的那样。设计者的初衷应该是并行的,因为每个Session有个对应的file,而不是公用一个文件。就因为上面的代码段,一个并行的初衷被扼杀了,不知道R&D的同事怎么考虑的。

        我自己试着改了该代码,将所有文件读写的工作交给了FileSessionData.java,

FileSessionData.java
 1   /*package*/ void syncSession(FileSessionData data, File saveTo) {
 2       if (!isValid()) return;
 3       
 4       DataOutputStream os = null;
 5       String id = data.id;
 6       synchronized(this)
 7       {
 8           try {
 9               os = new DataOutputStream(new FileOutputStream(saveTo));
10               WLObjectOutputStream oos = new WLObjectOutputStream(os);
11               oos.setReplacer(RemoteObjectReplacer.getReplacer());
12               oos.writeObject(data);
13               oos.flush();
14               os.writeLong(data.getLastAccessedTime());
15               oos.close();
16               os.close();
17             os = null;
18             if (verbose) HTTPSessionLogger.logPickledSession(id, saveTo.getAbsolutePath());
19           } catch (ThreadDeath td) {
20             throw td;
21           } catch (Throwable e) {
22             HTTPSessionLogger.logErrorSavingSessionData(e);
23             if (saveTo != null) saveTo.delete();
24           } finally {
25             if (os != null) {
26               try { os.close(); } catch (Exception ignore) {}
27             }
28           }
29         }
30   }


        同样loadSession()也被挪到了FileSeesionData, FileSessionContext只调用FileSessionData的接口就可以了。虽让这样可以做到并行写入,但整体性能还是不如JDBC。同样是文件写入, DB毕竟是异步写入的。提交到内存,然后由DBWn和LGWn完成具体的写入工作,所以性能上要好很多。

        这样的实现在JDBCSessionData中可以看到,JDBC相关的工作并没有交给JDBCSessionContext去完成。所以JDBC性能要比File要好很多,要使用JDBC store, 配置如下:
weblogic.xml
     <session-param>
        <param-name>PersistentStoreType</param-name>
        <param-value>jdbc</param-value>
      </session-param>
      <session-param>
        <param-name>PersistentStorePool</param-name>
        <param-value>SessionPool</param-value>
      </session-param>
      <session-param>
        <param-name>CacheSize</param-name>
        <param-value>0</param-value>
      </session-param>

       要使用JDBC persitence, 需要在SessionPool(jdbc connection pool)下创建wl_servlet_sessions,参考如下链接:
       http://e-docs.bea.com/wls/docs81/webapp/sessions.html

       好了,这里在提一下cache size的作用。CacheSize用于设定在使用jdbc或file persitence时,内存中cached的session数目。如果cache size为0,ServletRequest结束的时候,该session会被SessionContext从session reference pool中remove掉,这样该Session就成了内存垃圾, GC的时候会被回收掉。 下次请求进来的时候,Session会从persistence store中load出来。这样的话,应用性能会有问题。所以生产系统上建议设定一个cache size, 这样内存中可以cache一些session, 避免反复load seesion导致的新能问题。ServletRequest结束的时候,如果cache中还有空余空间(ArrayList),将该session置入,如果空间以满,则将最早的那个session给remove掉,而置入新进的session。在设定cache size的应用中,内存中包括两部分session,cache的session和当前open的session(正被引用的session,一般是ServletRequest还没有结束的session).。 注意:在控制台上看到的session数,不是表示当前内存中的session个数,而是persistence store(JDBC, File)中的session数, 即所有当前没有timeout的session。







posted on 2008-09-27 17:18 走走停停又三年 阅读(3640) 评论(2)  编辑  收藏 所属分类: Weblogic

FeedBack:
# re: Weblogic session persistence的性能的问题
2008-09-27 21:07 | YYX
作为一台电脑来说,硬盘的写入资源是唯一的,所以串行写文件没什么不对,如果并行写,N个线程争抢硬盘写入,那反而慢。  回复  更多评论
  
# re: Weblogic session persistence的性能的问题
2008-09-29 13:41 | 走走停停又三年
的确,并行写文件不如预期的那么快,但相对于串行,大概还是有50%的提升。我做了一个测试,写10个25M的文件,串行要82秒的时间,而并行的每个线程不到53秒。结果如下:

singleThread writer takes: 82.328 secs!

multiThread writer takes: 51.016 secs!
multiThread writer takes: 51.5 secs!
multiThread writer takes: 52.047 secs!
multiThread writer takes: 52.453 secs!
multiThread writer takes: 52.531 secs!
multiThread writer takes: 52.781 secs!
multiThread writer takes: 52.828 secs!
multiThread writer takes: 52.844 secs!
multiThread writer takes: 52.86 secs!
multiThread writer takes: 53.219 secs!  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: