服务器端实现原理
Session在服务器端具体是怎么实现的呢?我们使用session的时候一般都是这么使用的requestgetSession()或者requestgetSession(true)
这个时候服务器就检查是不是已经存在对应的Session对象见HttpRequestBase类
doGetSession(boolean create)方法
if ((session != null) && !sessionisValid())
session = null;
if (session != null)
return (sessiongetSession())
// Return the requested session if it exists and is valid
Manager manager = null;
if (context != null)
manager = contextgetManager()
if (manager == null)
return (null) // Sessions are not supported
if (requestedSessionId != null) {
try {
session = managerfindSession(requestedSessionId)
} catch (IOException e) {
session = null;
}
if ((session != null) && !sessionisValid())
session = null;
if (session != null) {
return (sessiongetSession())
}
}
requestSessionId从哪里来呢?这个肯定是通过Session实现机制的cookie或URL重写来设置的见HttpProcessor类中的parseHeaders(SocketInputStream input)
for (int i = ; i < cookieslength; i++) {
if (cookies[i]getName()equals
(GlobalsSESSION_COOKIE_NAME)) {
// Override anything requested in the URL
if (!requestisRequestedSessionIdFromCookie()) {
// Accept only the first session id cookie
requestsetRequestedSessionId
(cookies[i]getValue())
requestsetRequestedSessionCookie(true)
requestsetRequestedSessionURL(false)
}
}
}
或者HttpOrocessor类中的parseRequest(SocketInputStream input OutputStream output)
// Parse any requested session ID out of the request URI
int semicolon = uriindexOf(match) //match 是;jsessionid=字符串
if (semicolon >= ) {
String rest = urisubstring(semicolon + matchlength())
int semicolon = restindexOf(;)
if (semicolon >= ) {
requestsetRequestedSessionId(restsubstring( semicolon))
rest = restsubstring(semicolon)
} else {
requestsetRequestedSessionId(rest)
rest = ;
}
requestsetRequestedSessionURL(true)
uri = urisubstring( semicolon) + rest;
if (debug >= )
log( Requested URL session id is +
((HttpServletRequest) requestgetRequest())
getRequestedSessionId())
} else {
requestsetRequestedSessionId(null)
requestsetRequestedSessionURL(false)
}
里面的managerfindSession(requestSessionId)用于查找此会话ID对应的session对象Tomcat实现是通过一个HashMap实现见ManagerBasejava的findSession(String id)
if (id == null)
return (null)
synchronized (sessions) {
Session session = (Session) sessionsget(id)
return (session)
}
Session本身也是实现为一个HashMap因为Session设计为存放keyvalue键值对Tomcat里面Session实现类是StandardSession里面一个attributes属性
/**
* The collection of user data attributes associated with this Session
*/
private HashMap attributes = new HashMap()
所有会话信息的存取都是通过这个属性来实现的Session会话信息不会一直在服务器端保存超过一定的时间期限就会被删除这个时间期限可以在 webxml中进行设置不设置的话会有一个默认值Tomcat的默认值是那么服务器端是怎么判断会话过期的呢?原理服务器会启动一个线程一直查询所有的Session对象检查不活动的时间是否超过设定值如果超过就将其删除见StandardManager类它实现了Runnable 接口里面的run方法如下
/**
* The background thread that checks for session timeouts and shutdown
*/
public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep()
proces***pires()
}
}
/**
* Invalidate all sessions that have expired
*/
private void proces***pires() {
long timeNow = SystemcurrentTimeMillis()
Session sessions[] = findSessions()
for (int i = ; i < sessionslength; i++) {
StandardSession session = (StandardSession) sessions[i];
if (!sessionisValid())
continue;
int maxInactiveInterval = sessiongetMaxInactiveInterval()
if (maxInactiveInterval < )
continue;
int timeIdle = // Truncate do not round up
(int) ((timeNow sessiongetLastUsedTime()) / L)
if (timeIdle >= maxInactiveInterval) {
try {
expiredSessions++;
sessionexpire()
} catch (Throwable t) {
log(smgetString(standardManagerexpireException) t)
}
}
}
}
Session 信息在createexpire等事情的时候都会触发相应的Listener事件从而可以对session信息进行监控这些Listener只需要继承HttpSessionListener并配置在webxml文件中如下是一个监控在线会话数的Listerner:
import javautilHashSet;
import javaxservletServletContext;
import javaxservlethttpHttpSession;
import javaxservlethttpHttpSessionEvent;
import javaxservlethttpHttpSessionListener;
public class MySessionListener implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent event) {
HttpSession session = eventgetSession()
ServletContext application = sessiongetServletContext()
// 在application范围由一个HashSet集保存所有的session
HashSet sessions = (HashSet) applicationgetAttribute(sessions)
if (sessions == null) {
sessions = new HashSet()
applicationsetAttribute(sessions sessions)
}
// 新创建的session均添加到HashSet集中
sessionsadd(session)
// 可以在别处从application范围中取出sessions集合
// 然后使用sessionssize()获取当前活动的session数即为在线人数
}
public void sessionDestroyed(HttpSessionEvent event) {
HttpSession session = eventgetSession()
ServletContext application = sessiongetServletContext()
HashSet sessions = (HashSet) applicationgetAttribute(sessions)
// 销毁的session均从HashSet集中移除
sessionsremove(session)
}
}