tomcat session实现原理

服务器端实现原理

Session在服务器端具体是怎么实现的呢?我们使用session的时候一般都是这么使用的:

request.getSession()或者request.getSession(true)。

这个时候,服务器就检查是不是已经存在对应的Session对象,见HttpRequestBase类
doGetSession(boolean create)方法:

 1 if ((session != null) && !session.isValid())
 2 session = null;
 3 if (session != null)
 4 return (session.getSession());
 5  
 6  
 7 // Return the requested session if it exists and is valid
 8 Manager manager = null;
 9 if (context != null)
10 manager = context.getManager();
11 if (manager == null)
12 return (null); // Sessions are not supported
13 if (requestedSessionId != null) {
14 try {
15 session = manager.findSession(requestedSessionId);
16 } catch (IOException e) {
17 session = null;
18 }
19 if ((session != null) && !session.isValid())
20 session = null;
21 if (session != null) {
22 return (session.getSession());
23 }
24 }

requestSessionId从哪里来呢 ?这个肯定是通过Session实现机制的cookie或URL重写来设置的。见HttpProcessor类中的parseHeaders(SocketInputStream input):

 1 for (int i = 0; i < cookies.length; i++) {
 2 if (cookies[i].getName().equals
 3 (Globals.SESSION_COOKIE_NAME)) {
 4 // Override anything requested in the URL
 5 if (!request.isRequestedSessionIdFromCookie()) {
 6 // Accept only the first session id cookie
 7 request.setRequestedSessionId
 8 (cookies[i].getValue());
 9 request.setRequestedSessionCookie(true);
10 request.setRequestedSessionURL(false);
11   
12 }
13 }
14 }
或者HttpOrocessor类中的parseRequest(SocketInputStream input, OutputStream output)

 1 // Parse any requested session ID out of the request URI
 2 int semicolon = uri.indexOf(match); //match 是";jsessionid="字符串
 3 if (semicolon >= 0) {
 4 String rest = uri.substring(semicolon + match.length());
 5 int semicolon2 = rest.indexOf(';');
 6 if (semicolon2 >= 0) {
 7 request.setRequestedSessionId(rest.substring(0, semicolon2));
 8 rest = rest.substring(semicolon2);
 9 } else {
10 request.setRequestedSessionId(rest);
11 rest = "";
12 }
13 request.setRequestedSessionURL(true);
14 uri = uri.substring(0, semicolon) + rest;
15 if (debug >= 1)
16 log(" Requested URL session id is " +
17 ((HttpServletRequest) request.getRequest())
18 .getRequestedSessionId());
19 } else {
20 request.setRequestedSessionId(null);
21 request.setRequestedSessionURL(false);
22 }
23  

里面的manager.findSession(requestSessionId)用于查找此会话ID对应的session对象。 Tomcat实现
是通过一个HashMap实现
,见ManagerBase.java的findSession(String id):

1 if (id == null)
2 return (null);
3 synchronized (sessions) {
4 Session session = (Session) sessions.get(id);
5 return (session);
6 }
Session本身也是实现为一个HashMap ,因为Session设计为存放key-value键值对,Tomcat里面Session实现类是StandardSession,里面一个attributes属性:

1 /**
2 * The collection of user data attributes associated with this Session.
3 */
4 private HashMap attributes = new HashMap();
所 有会话信息的存取都是通过这个属性来实现的。Session会话信息不会一直在服务器端保存,超过一定的时间期限就会被删除,这个时间期限可以在 web.xml中进行设置,不设置的话会有一个默认值,Tomcat的默认值是60。那么服务器端是怎么判断会话过期的呢?原理服务器会启动一个线程,一 直查询所有的Session对象,检查不活动的时间是否超过设定值,如果超过就将其删除。见StandardManager类,它实现了Runnable 接口,里面的run方法如下:

 1 /**
 2 * The background thread that checks for session timeouts and shutdown.
 3 */
 4 public void run() {
 5  
 6 // Loop until the termination semaphore is set
 7 while (!threadDone) {
 8 threadSleep();
 9 proces***pires();
10 }
11  
12 }
13  
14 /**
15 * Invalidate all sessions that have expired.
16 */
17 private void proces***pires() {
18  
19 long timeNow = System.currentTimeMillis();
20 Session sessions[] = findSessions();
21  
22 for (int i = 0; i < sessions.length; i++) {
23 StandardSession session = (StandardSession) sessions[i];
24 if (!session.isValid())
25 continue;
26 int maxInactiveInterval = session.getMaxInactiveInterval();
27 if (maxInactiveInterval < 0)
28 continue;
29 int timeIdle = // Truncate, do not round up
30 (int) ((timeNow - session.getLastUsedTime()) / 1000L);
31 if (timeIdle >= maxInactiveInterval) {
32 try {
33 expiredSessions++;
34 session.expire();
35 } catch (Throwable t) {
36 log(sm.getString("standardManager.expireException"), t);
37 }
38 }
39 }
40  
41 }
Session 信息在create,expire等事情的时候都会触发相应的Listener事件,从而可以对session信息进行监控,这些Listener只需要 继承HttpSessionListener,并配置在web.xml文件中。如下是一个监控在线会话数的Listerner:

import java.util.HashSet;

import javax.servlet.ServletContext;

import javax.servlet.http.HttpSession;

import javax.servlet.http.HttpSessionEvent;

import javax.servlet.http.HttpSessionListener;  

public class MySessionListener implements HttpSessionListener {   

 public void sessionCreated(HttpSessionEvent event) {   

 HttpSession session = event.getSession();   

ServletContext application = session.getServletContext();   

 // 在application范围由一个HashSet集保存所有的session   

 HashSet sessions = (HashSet) application.getAttribute("sessions");   

if (sessions == null) {   

sessions = new HashSet();   

application.setAttribute("sessions", sessions);   

}   

// 新创建的session均添加到HashSet集中   

 sessions.add(session);   

// 可以在别处从application范围中取出sessions集合   

// 然后使用sessions.size()获取当前活动的session数,即为“在线人数”   

}   

public void sessionDestroyed(HttpSessionEvent event) {   

HttpSession session = event.getSession();   

 ServletContext application = session.getServletContext();   

 HashSet sessions = (HashSet) application.getAttribute("sessions");   

 // 销毁的session均从HashSet集中移除   

sessions.remove(session);   

}

}
服务器端实现原理

Session在服务器端具体是怎么实现的呢?我们使用session的时候一般都是这么使用的:

request.getSession()或者request.getSession(true)。

这个时候,服务器就检查是不是已经存在对应的Session对象,见HttpRequestBase类
doGetSession(boolean create)方法:

 1 if ((session != null) && !session.isValid())
 2 session = null;
 3 if (session != null)
 4 return (session.getSession());
 5  
 6  
 7 // Return the requested session if it exists and is valid
 8 Manager manager = null;
 9 if (context != null)
10 manager = context.getManager();
11 if (manager == null)
12 return (null); // Sessions are not supported
13 if (requestedSessionId != null) {
14 try {
15 session = manager.findSession(requestedSessionId);
16 } catch (IOException e) {
17 session = null;
18 }
19 if ((session != null) && !session.isValid())
20 session = null;
21 if (session != null) {
22 return (session.getSession());
23 }
24 }

requestSessionId从哪里来呢 ?这个肯定是通过Session实现机制的cookie或URL重写来设置的。见HttpProcessor类中的parseHeaders(SocketInputStream input):

 1 for (int i = 0; i < cookies.length; i++) {
 2 if (cookies[i].getName().equals
 3 (Globals.SESSION_COOKIE_NAME)) {
 4 // Override anything requested in the URL
 5 if (!request.isRequestedSessionIdFromCookie()) {
 6 // Accept only the first session id cookie
 7 request.setRequestedSessionId
 8 (cookies[i].getValue());
 9 request.setRequestedSessionCookie(true);
10 request.setRequestedSessionURL(false);
11   
12 }
13 }
14 }
或者HttpOrocessor类中的parseRequest(SocketInputStream input, OutputStream output)

 1 // Parse any requested session ID out of the request URI
 2 int semicolon = uri.indexOf(match); //match 是";jsessionid="字符串
 3 if (semicolon >= 0) {
 4 String rest = uri.substring(semicolon + match.length());
 5 int semicolon2 = rest.indexOf(';');
 6 if (semicolon2 >= 0) {
 7 request.setRequestedSessionId(rest.substring(0, semicolon2));
 8 rest = rest.substring(semicolon2);
 9 } else {
10 request.setRequestedSessionId(rest);
11 rest = "";
12 }
13 request.setRequestedSessionURL(true);
14 uri = uri.substring(0, semicolon) + rest;
15 if (debug >= 1)
16 log(" Requested URL session id is " +
17 ((HttpServletRequest) request.getRequest())
18 .getRequestedSessionId());
19 } else {
20 request.setRequestedSessionId(null);
21 request.setRequestedSessionURL(false);
22 }
23  

里面的manager.findSession(requestSessionId)用于查找此会话ID对应的session对象。 Tomcat实现
是通过一个HashMap实现
,见ManagerBase.java的findSession(String id):

1 if (id == null)
2 return (null);
3 synchronized (sessions) {
4 Session session = (Session) sessions.get(id);
5 return (session);
6 }
Session本身也是实现为一个HashMap ,因为Session设计为存放key-value键值对,Tomcat里面Session实现类是StandardSession,里面一个attributes属性:

1 /**
2 * The collection of user data attributes associated with this Session.
3 */
4 private HashMap attributes = new HashMap();
所 有会话信息的存取都是通过这个属性来实现的。Session会话信息不会一直在服务器端保存,超过一定的时间期限就会被删除,这个时间期限可以在 web.xml中进行设置,不设置的话会有一个默认值,Tomcat的默认值是60。那么服务器端是怎么判断会话过期的呢?原理服务器会启动一个线程,一 直查询所有的Session对象,检查不活动的时间是否超过设定值,如果超过就将其删除。见StandardManager类,它实现了Runnable 接口,里面的run方法如下:

 1 /**
 2 * The background thread that checks for session timeouts and shutdown.
 3 */
 4 public void run() {
 5  
 6 // Loop until the termination semaphore is set
 7 while (!threadDone) {
 8 threadSleep();
 9 proces***pires();
10 }
11  
12 }
13  
14 /**
15 * Invalidate all sessions that have expired.
16 */
17 private void proces***pires() {
18  
19 long timeNow = System.currentTimeMillis();
20 Session sessions[] = findSessions();
21  
22 for (int i = 0; i < sessions.length; i++) {
23 StandardSession session = (StandardSession) sessions[i];
24 if (!session.isValid())
25 continue;
26 int maxInactiveInterval = session.getMaxInactiveInterval();
27 if (maxInactiveInterval < 0)
28 continue;
29 int timeIdle = // Truncate, do not round up
30 (int) ((timeNow - session.getLastUsedTime()) / 1000L);
31 if (timeIdle >= maxInactiveInterval) {
32 try {
33 expiredSessions++;
34 session.expire();
35 } catch (Throwable t) {
36 log(sm.getString("standardManager.expireException"), t);
37 }
38 }
39 }
40  
41 }
Session 信息在create,expire等事情的时候都会触发相应的Listener事件,从而可以对session信息进行监控,这些Listener只需要 继承HttpSessionListener,并配置在web.xml文件中。如下是一个监控在线会话数的Listerner:

import java.util.HashSet;

import javax.servlet.ServletContext;

import javax.servlet.http.HttpSession;

import javax.servlet.http.HttpSessionEvent;

import javax.servlet.http.HttpSessionListener;  

public class MySessionListener implements HttpSessionListener {   

 public void sessionCreated(HttpSessionEvent event) {   

 HttpSession session = event.getSession();   

ServletContext application = session.getServletContext();   

 // 在application范围由一个HashSet集保存所有的session   

 HashSet sessions = (HashSet) application.getAttribute("sessions");   

if (sessions == null) {   

sessions = new HashSet();   

application.setAttribute("sessions", sessions);   

}   

// 新创建的session均添加到HashSet集中   

 sessions.add(session);   

// 可以在别处从application范围中取出sessions集合   

// 然后使用sessions.size()获取当前活动的session数,即为“在线人数”   

}   

public void sessionDestroyed(HttpSessionEvent event) {   

HttpSession session = event.getSession();   

 ServletContext application = session.getServletContext();   

 HashSet sessions = (HashSet) application.getAttribute("sessions");   

 // 销毁的session均从HashSet集中移除   

sessions.remove(session);   

}

}

猜你喜欢

转载自blog.csdn.net/u014398624/article/details/78496696