Tomcat中的session是如何管理的?
程序员文章站
2022-07-15 16:35:59
...
最近有个朋友问我:
答:内存中。又问:
答:(当时没看过这块的代码),说由于每个应用对应一个Session管理,此处应该有类似于数组之类的东西,将该应用的session管理起来,当该应用移除或者某些sesion过期时可以准确的删除掉。最近看了下代码,对比下源码,发现回复的还行,没有特别大的出入。
我们的应用中一般都会用到session,那这个session在应用服务器内部到底是怎么保存到处理的呢?
当我们请求一个应用时,如果页面中用到了session,此时的创建session的调用链如下:
以上调用链的部分关键代码:
在setId时,同时在Manager中将相应的session保存了下来。此处对于session的保存使用的是ConcurrentHashMap,在创建后将其添加到map中,session过期后将其移除。
而在停止一个应用时,相应的remove session的调用链如下:
其中执行remove的代码如下:
引用
tomcat的session存在哪里?
答:内存中。又问:
引用
那既然都保存在内存中,要清除过期的sesion时,又怎么能区分是哪个应用的session而不会清除错误?
答:(当时没看过这块的代码),说由于每个应用对应一个Session管理,此处应该有类似于数组之类的东西,将该应用的session管理起来,当该应用移除或者某些sesion过期时可以准确的删除掉。最近看了下代码,对比下源码,发现回复的还行,没有特别大的出入。
我们的应用中一般都会用到session,那这个session在应用服务器内部到底是怎么保存到处理的呢?
当我们请求一个应用时,如果页面中用到了session,此时的创建session的调用链如下:
Daemon Thread [http-bio-8090-exec-1] (Suspended (breakpoint at line 145 in SessionIdGenerator)) owns: SocketWrapper<E> (id=234) SessionIdGenerator.generateSessionId() line: 145 StandardManager(ManagerBase).generateSessionId() line: 807 StandardManager(ManagerBase).createSession(String) line: 653 Request.doGetSession(boolean) line: 2892 Request.getSession(boolean) line: 2315 RequestFacade.getSession(boolean) line: 898 RequestFacade.getSession() line: 910 PageContextImpl._initialize(Servlet, ServletRequest, ServletResponse, String, boolean, int, boolean) line: 146 PageContextImpl.initialize(Servlet, ServletRequest, ServletResponse, String, boolean, int, boolean) line: 125 JspFactoryImpl.internalGetPageContext(Servlet, ServletRequest, ServletResponse, String, boolean, int, boolean) line: 112 JspFactoryImpl.getPageContext(Servlet, ServletRequest, ServletResponse, String, boolean, int, boolean) line: 65 index.jsp line: not available index_jsp(HttpJspBase).service(HttpServletRequest, HttpServletResponse) line: 70 index_jsp(HttpServlet).service(ServletRequest, ServletResponse) line: 728 JspServletWrapper.service(HttpServletRequest, HttpServletResponse, boolean) line: 432 JspServlet.serviceJspFile(HttpServletRequest, HttpServletResponse, String, boolean) line: 390 JspServlet.service(HttpServletRequest, HttpServletResponse) line: 334 JspServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 728 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 305 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210 StandardWrapperValve.invoke(Request, Response) line: 222 StandardContextValve.invoke(Request, Response) line: 123 NonLoginAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 472 StandardHostValve.invoke(Request, Response) line: 171 ErrorReportValve.invoke(Request, Response) line: 99 AccessLogValve.invoke(Request, Response) line: 936 StandardEngineValve.invoke(Request, Response) line: 118 CoyoteAdapter.service(Request, Response) line: 408 Http11Processor(AbstractHttp11Processor<S>).process(SocketWrapper<S>) line: 1009 Http11Protocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler<S,P>).process(SocketWrapper<S>, SocketStatus) line: 589 JIoEndpoint$SocketProcessor.run() line: 310 ThreadPoolExecutor$Worker.runTask(Runnable) line: 895 ThreadPoolExecutor$Worker.run() line: 918 TaskThread(Thread).run() line: 662
以上调用链的部分关键代码:
/** * Construct and return a new session object, based on the default * settings specified by this Manager's properties. The session * id specified will be used as the session id. * If a new session cannot be created for any reason, return * <code>null</code>. * * @param sessionId The session id which should be used to create the * new session; if <code>null</code>, a new session id will be * generated * @exception IllegalStateException if a new session cannot be * instantiated for any reason */ @Override public Session createSession(String sessionId) { if ((maxActiveSessions >= 0) && (getActiveSessions() >= maxActiveSessions)) { rejectedSessions++; throw new TooManyActiveSessionsException( sm.getString("managerBase.createSession.ise"), maxActiveSessions); } // Recycle or create a Session instance Session session = createEmptySession(); // Initialize the properties of the new session and return it session.setNew(true); session.setValid(true); session.setCreationTime(System.currentTimeMillis()); session.setMaxInactiveInterval(this.maxInactiveInterval); String id = sessionId; if (id == null) { id = generateSessionId(); //此处生成sessionID } session.setId(id); sessionCounter++; SessionTiming timing = new SessionTiming(session.getCreationTime(), 0); synchronized (sessionCreationTiming) { sessionCreationTiming.add(timing); sessionCreationTiming.poll(); } return (session); } public void setId(String id, boolean notify) { if ((this.id != null) && (manager != null)) manager.remove(this); this.id = id; if (manager != null) manager.add(this); //注意此处 if (notify) { tellNew(); } }
在setId时,同时在Manager中将相应的session保存了下来。此处对于session的保存使用的是ConcurrentHashMap,在创建后将其添加到map中,session过期后将其移除。
/** * Add this Session to the set of active Sessions for this Manager. * * @param session Session to be added */ @Override public void add(Session session) { sessions.put(session.getIdInternal(), session); int size = getActiveSessions(); //此处应该可以使用AtomicInteger替换掉。 if( size > maxActive ) { synchronized(maxActiveUpdateLock) { if( size > maxActive ) { maxActive = size; } } } }
而在停止一个应用时,相应的remove session的调用链如下:
Daemon Thread [http-bio-8090-exec-2] (Suspended (breakpoint at line 733 in ManagerBase)) owns: StandardSession (id=340) owns: StandardManager (id=326) owns: StandardContext (id=327) owns: SocketWrapper<E> (id=341) StandardManager(ManagerBase).remove(Session, boolean) line: 733 StandardSession.expire(boolean) line: 840 StandardManager.doUnload() line: 463 StandardManager.unload() line: 353 StandardManager.stopInternal() line: 518 StandardManager(LifecycleBase).stop() line: 232 StandardContext.stopInternal() line: 5569 StandardContext(LifecycleBase).stop() line: 232 HTMLManagerServlet(ManagerServlet).stop(PrintWriter, ContextName, StringManager) line: 1306 HTMLManagerServlet.stop(ContextName, StringManager) line: 733 HTMLManagerServlet.doPost(HttpServletRequest, HttpServletResponse) line: 221 HTMLManagerServlet(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 647 HTMLManagerServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 728 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 305 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210 CsrfPreventionFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 213 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 243 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210 SetCharacterEncodingFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 108 ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 243 ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210 StandardWrapperValve.invoke(Request, Response) line: 222 StandardContextValve.invoke(Request, Response) line: 123 BasicAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 581 StandardHostValve.invoke(Request, Response) line: 171 ErrorReportValve.invoke(Request, Response) line: 99 AccessLogValve.invoke(Request, Response) line: 936 StandardEngineValve.invoke(Request, Response) line: 118 CoyoteAdapter.service(Request, Response) line: 408 Http11Processor(AbstractHttp11Processor<S>).process(SocketWrapper<S>) line: 1009 Http11Protocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler<S,P>).process(SocketWrapper<S>, SocketStatus) line: 589 JIoEndpoint$SocketProcessor.run() line: 310 ThreadPoolExecutor$Worker.runTask(Runnable) line: 895 ThreadPoolExecutor$Worker.run() line: 918 TaskThread(Thread).run() line: 662
其中执行remove的代码如下:
/** * Perform the internal processing required to invalidate this session, * without triggering an exception if the session has already expired. * * @param notify Should we notify listeners about the demise of * this session? */ public void expire(boolean notify) { // Check to see if expire is in progress or has previously been called if (expiring || !isValid) return; synchronized (this) { // Check again, now we are inside the sync so this code only runs once // Double check locking - expiring and isValid need to be volatile if (expiring || !isValid) return; if (manager == null) return; ... //省略部分代码 } ... //省略部分代码 if (ACTIVITY_CHECK) { accessCount.set(0); } setValid(false); // Remove this session from our manager's active sessions manager.remove(this, true); } StandardManager中的remove方法: /** * Remove this Session from the active Sessions for this Manager. * * @param session Session to be removed * @param update Should the expiration statistics be updated */ @Override public void remove(Session session, boolean update) { // If the session has expired - as opposed to just being removed from // the manager because it is being persisted - update the expired stats if (update) { long timeNow = System.currentTimeMillis(); int timeAlive = (int) (timeNow - session.getCreationTimeInternal())/1000; updateSessionMaxAliveTime(timeAlive); expiredSessions.incrementAndGet(); SessionTiming timing = new SessionTiming(timeNow, timeAlive); synchronized (sessionExpirationTiming) { sessionExpirationTiming.add(timing); sessionExpirationTiming.poll(); } } if (session.getIdInternal() != null) { sessions.remove(session.getIdInternal()); //注意此处,即为上面保存session的concurrentHashMap } }
上一篇: Tomcat的NIO 连接器中oomParachute的作用
下一篇: Tomcat官方文档中文版
推荐阅读
-
宋代瓦舍简介,《东京梦华录》中是如何描述瓦舍的?
-
如何解决Ajax访问不断变化的session的值不一致以及HTTP协议中的GET、POST的区别
-
Mybaits 源码解析 (六)----- 全网最详细:Select 语句的执行过程分析(上篇)(Mapper方法是如何调用到XML中的SQL的?)
-
刘氏是如何在吕氏专权中夺回他们的*的?谁的功劳最大呢?
-
白马之战爆发的经过是怎样的?关羽在此战中的表现如何?
-
在Excel中=是如何不简单的等号附5个=绝妙使用技巧
-
Word文档中的默认字体是宋体如何将其改为微软雅黑
-
微信小程序授权 获取用户的openid和session_key【后端使用java语言编写】,我写的是get方式,目的是测试能否获取到微信服务器中的数据,后期我会写上post请求方式。
-
vue中如何实现后台管理系统的权限控制的方法示例
-
明朝开国第一文臣李善长,是如何从历史中谢幕的?