Tomcat源码系列5--Tomcat的Session生成和管理1
本次主要谈一下Tomcat中Session的生成和管理情况。
一. 关于Sessin和cookie
session被用于表示一个持续的连接状态,在网站访问中一般指代客户端浏览器的进程从开启到结束的过程。
session的常见实现形式有两种:
1. 会话cookie:即未设置过期时间的cookie,这个cookie的默认生命周期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。
2. 持久cookie:是指存放于客户端硬盘中的 cookie信息(设置了一定的有效期限),它不会随着浏览器的关闭而消失。
实现机制是当用户发起一个请求的时候,服务器会检查该请求中是否包含sessionid,如果未包含,则系统会创造一个名为JSESSIONID的输出cookie返回给浏览器,并将其以HashTable的形式写到服务器的内存里面; 当已经包含sessionid是,服务端会检查找到与该session相匹配的信息,如果存在则直接使用该sessionid,若不存在则重新生成新的session。
二. Session的生成
1.当tomcat处理请求时会调用org.apache.catalina.connector.CoyoteAdapter.service()方法。
(org.apache.catalina.connector.CoyoteAdapter.service)public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) throws Exception { ……//此处省略一部分代码 try { if ( postParseRequest(req, request, res, response) ) { connector.getContainer().getPipeline().getFirst().invoke(request, response); } response.finishResponse(); req.action( ActionCode.ACTION_POST_REQUEST , null); } catch (IOException e) { ; } catch (Throwable t) { log.error(sm.getString("coyoteAdapter.service"), t); } finally { request.recycle(); response.recycle(); } }
此时CoyoteAdapter.service()方法会调用postParseRequest()方法。
2.postParseRequest()方法会分别调用parseSessionId(),parseSessionCookiesId()两个方法对JSessionid进行解析。
org.apache.catalina.connector.CoyoteAdapter.postParseRequest()protected boolean postParseRequest(org.apache.coyote.Request req, Request request, org.apache.coyote.Response res, Response response) throws Exception { //此处省略部分代码 // 解析session Id parseSessionId(req, request);§2.1 //此处省略部分代码 // 解析session Id parseSessionCookiesId(req, request); §2.2 return true; }
§2.1
从URL中读取sessionID。
(org.apache.catalina.connector.CoyoteAdapter.parseSessionId)protected void parseSessionId(org.apache.coyote.Request req, Request request) { CharChunk uriCC = req.decodedURI().getCharChunk(); // 判断URL中是否有“;jsessionid=”这个字符串 int semicolon = uriCC.indexOf(match, 0, match.length(), 0); if (semicolon > 0) { // 解析 session ID,并从URL中提取它 int start = uriCC.getStart(); int end = uriCC.getEnd(); int sessionIdStart = start + semicolon + match.length(); int semicolon2 = uriCC.indexOf(';', sessionIdStart); if (semicolon2 >= 0) { request.setRequestedSessionId (new String(uriCC.getBuffer(), sessionIdStart, semicolon2 - semicolon - match.length())); } else { request.setRequestedSessionId (new String(uriCC.getBuffer(), sessionIdStart, end - sessionIdStart)); } request.setRequestedSessionURL(true); //从请求的URL中提取session ID ByteChunk uriBC = req.requestURI().getByteChunk(); start = uriBC.getStart(); end = uriBC.getEnd(); semicolon = uriBC.indexOf(match, 0, match.length(), 0); if (semicolon > 0) { sessionIdStart = start + semicolon; semicolon2 = uriCC.indexOf (';', start + semicolon + match.length()); uriBC.setEnd(start + semicolon); byte[] buf = uriBC.getBuffer(); if (semicolon2 >= 0) { for (int i = 0; i < end - start - semicolon2; i++) { buf[start + semicolon + i] = buf[start + i + semicolon2]; } uriBC.setBytes(buf, start, semicolon + (end - start - semicolon2)); } } } else { request.setRequestedSessionId(null); request.setRequestedSessionURL(false); } }
§2.2
从Cookie中读取sessionID。
(org.apache.catalina.connector.CoyoteAdapter.parseSessionCookiesId)protected void parseSessionCookiesId(org.apache.coyote.Request req, Request request) { // 从cookie中解析session ID Cookies serverCookies = req.getCookies(); int count = serverCookies.getCookieCount(); if (count <= 0) return; for (int i = 0; i < count; i++) { ServerCookie scookie = serverCookies.getCookie(i); if (scookie.getName().equals(Globals.SESSION_COOKIE_NAME)) { //重写URL中的任何请求 if (!request.isRequestedSessionIdFromCookie()) { // 只接受第一个session ID的Cookie request.setRequestedSessionId (scookie.getValue().toString()); request.setRequestedSessionCookie(true); request.setRequestedSessionURL(false); if (log.isDebugEnabled()) log.debug(" Requested cookie session id is " + request.getRequestedSessionId()); } else { if (!request.isRequestedSessionIdValid()) { //替换,直到一个会话ID是有效的 request.setRequestedSessionId (scookie.getValue().toString()); }}}}}
当parseSessionId()、parseSessionCookiesId()两个方法调用结束后,postParseRequest方法也调用结束,此时session ID的获取过程结束,下一步程序会根据session ID来得到session。
3.当要生成一个session对象时,getSession(booleancreate)方法将提高调用。
(org.apache.catalina.connector.Request.getSession(boolean create))public HttpSession getSession(boolean create) { Session session = doGetSession(create); if (session != null) { return session.getSession(); } else { return null; } }
4.Request#doGetSession被调用,方法就sessionid进行查找,查找成功则返回session对象,查找不成功就创建一个新的session对象,并在响应客户请求时创建sessionCookie发给至客户端。
(org.apache.catalina.connector.Request.doGetSession(boolean create))protected Session doGetSession(boolean create) { //context不存在就直接返回null if (context == null) return (null); //判断session是否有效 if ((session != null) && !session.isValid()) session = null; if (session != null) return (session); //如果存在和有效返回请求的session Manager manager = null; if (context != null) manager = context.getManager(); if (manager == null) return (null); // Sessions 不被支持 // 判断是否有SessionID if (requestedSessionId != null) { try { // 在manager中根据sessionid查找session session = manager.findSession(requestedSessionId);※1 } catch (IOException e) { session = null; } if ((session != null) && !session.isValid()) session = null; if (session != null) { //更新访问时间 session.access(); return (session); } } // 如果请求和响应没有被提交就创建一个新的session if (!create) return (null); if ((context != null) && (response != null) && context.getCookies() && response.getResponse().isCommitted()) { throw new IllegalStateException (sm.getString("coyoteRequest.sessionCreateCommitted")); } session = manager.createSession();※2 //根据session创建一个新的session Cookie if ((session != null) && (getContext() != null) && getContext().getCookies()) { Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME, session.getId()); // 配置Session Cookie configureSessionCookie(cookie); // 在响应中加入Session Cookie response.addCookie(cookie); } if (session != null) { //更新访问时间 session.access(); return (session); } else { return (null); } }
※1
根据传过来的id参数对session进行查找,查找即返回session对象,若没查找到就创建一个新的session对象。
(org.apache.catalina.session.ManagerBase.findSession(String id))public Session findSession(String id) throws IOException { if (id == null) return (null); synchronized (sessions) { Session session = (Session) sessions.get(id); return (session); } }
※2
ManagerBase#createSession()被调用,创建一个新的session。
(org.apache.catalina.session.ManagerBase.createSession())public Session createSession() { //回收或创建一个session实例 Session session = createEmptySession(); //初始化新session的属性并返回 session.setNew(true); session.setValid(true); session.setCreationTime(System.currentTimeMillis()); session.setMaxInactiveInterval(this.maxInactiveInterval); String sessionId = generateSessionId(); session.setId(sessionId); sessionCounter++; return (session); }
未完待续,下面谈一谈Session管理方面的内容。