设为首页 收藏本站
查看: 320|回复: 0

[经验分享] Tomcat中的session是如何管理的?

[复制链接]

尚未签到

发表于 2017-2-2 06:48:00 | 显示全部楼层 |阅读模式
最近有个朋友问我:引用
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
}
}

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-336290-1-1.html 上篇帖子: Tomcat中JSP引擎工作原理 下篇帖子: Tomcat下多项目Session共享
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表