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

[经验分享] 《How Tomcat Works》翻译(9) 之 会话管理

[复制链接]

尚未签到

发表于 2017-2-6 09:16:53 | 显示全部楼层 |阅读模式
第九章、会话管理


  一、前言部分
  Catalina通过一个叫做manager的组件来支持会话管理,它是由org.apache.catalina.Manager接口代表。A manager一直与context相关联,还有就是manager负责创建、更新、销毁会话对象同时也会返回任何组件所需要的有效的会话对象。
  一个servlet通过调用javax.servlet.http.HttpServletRequest接口中的getSession方法获得一个session对象,该接口是在默认连接器里由org.apache.catalina.connector.HttpRequestBase类实现的。这里在HttpRequestBase类中有一些相关的方法。

  public HttpSession getSession() {
return (getSession(true));
}
public HttpSession getSession(boolean create) {
if( System.getSecurityManager() != null ) {
PrivilegedGetSession dp = new PrivilegedGetSession(create);
return (HttpSession)AccessController.doPrivileged(dp);
}
return doGetSession(create);
}
private HttpSession doGetSession(boolean create) {
// There cannot be a session if no context has been assigned yet
if (context == null)
return (null);
// Return the current session if it exists and is valid
if ((session != null) && !session.isValid())
session = null;
if (session != null)
return (session.getSession());

// Return the requested session if it exists and is valid
Manager manager = null;
if (context != null)
manager = context.getManager();
if (manager == null)
return (null);      // Sessions are not supported
if (requestedSessionId != null) {
try {
session = manager.findSession(requestedSessionId);
} catch (IOException e) {
session = null;
}
if ((session != null) && !session.isValid())
session = null;
if (session != null) {
return (session.getSession());
}
}
// Create a new session if requested and the response is not committed
if (!create)
return (null);
if ((context != null) && (response != null) &&
context.getCookies() &&
response.getResponse().isCommitted()) {
throw new IllegalStateException
(sm.getString("httpRequestBase.createCommitted"));
}
session = manager.createSession();
if (session != null)
return (session.getSession());
else
return (null);
}

  默认下,一个manager把所有的sessions对象存储在内存中。然而,Tomcat也允许一个manager把会话对象存放在文件只能够或者数据库(通过JDBC)。Catalina提供了org.apache.catalina.session包,里面包含了会话对象和会话管理类型。
  这章从三个部分解释在Catalina中的会话管理:"Session", " Managers ",  " Stores ".最后部分就用Context关联manager来解释这个应用程序。
  二、会话
  在servlet程序设计中,一个会话对象由javax.servlet.http.HttpSession接口代表,此接口实现的类是StandardSession(在org.apache.catalina.session包中)。然而,对于安全考虑,manager不会传给一个StandardSession实例给servlet,相反它把自己封装了一次,该类的名字是StandardSessionFacade(在org.apache.catalina.session包中)。注解:看到没有又用来了门面模式啦。。。。
。实际上,manager与另外一个门面(org.apache.catalina.Session接口)协调工作。下面的UML图是关于会话相关的类型。注意为了使图精简我就把org.apache.catalina的前缀(Session, StandardSession,StandardSessionFacade)去掉.
  
DSC0000.png
  三、The Session 接口
  The Session 接口充当了Catalina内部门面角色。the Session接口的标准实现是StandardSession类,同时它也实现了javax.servlet.http.HttpSession接口,The Session接口下面的代码:

package org.apache.catalina;

import java.security.Principal;
import java.util.Iterator;
import javax.servlet.http.HttpSession;
public interface Session {
public static final String SESSION_CREATED_EVENT = "createSession";
public static final String SESSION_DESTROYED_EVENT = "destroySession";
public String getAuthType();
public void setAuthType(String authType);
public long getCreationTime();
public void setCreationTime(long time);
public String getId();
public void setId(String id);
public String getInfo();
public long getLastAccessedTime();
public Manager getManager();
public void setManager(Manager manager);
public int getMaxInactiveInterval();
public void setMaxInactiveInterval(int interval);
public void setNew(boolean isNew);
public Principal getPrincipal();
public void setPrincipal(Principal principal);
public HttpSession getSession();
public void setValid(boolean isValid);
public boolean isValid();
public void access();
public void addSessionListener(SessionListener listener);
public void expire();
public Object getNote(String name);
public Iterator getNoteNames();
public void recycle();
public void removeNote(String name);
public void removeSessionListener(SessionListener listener);
public void setNote(String name, Object value);

}

   一个会话对象一直包含在一个manager中,the setManager和getManager方法目的是一个会话实例与一个manager相关联. 一个会话实例同时也有一个唯一的标志符( 通过The Context相关联的manager),Session标识符也能够被the setId和getId方法访问。the manager决定一个会话对象的有效性来调用The getLastAccessedTime方法,The manager调用了setValid方法是设置或者是重新设置一个会话对象的有效性。只要每次会话实例被访问,那么其方法就会被调用最近更新时间。最后,The manager能够使得一个会话对象过期,通过调用the expire方法; the getSession方法返回一个门面封装了的HttpSession对象。
  四、The StandardSession类
  The StandardSession类是实现The Session接口的标准实现。除此之外他还实现了javax.servlet.http.HttpSession接口和java.lang.Serializable(这个是为了使得会话对象能够序列化)接口。
  The standardSession的构造函数接收一个Manager实例,目的是使得一个会话对象一直拥有一个Manager.
  public StandardSession(Manager manager);
  下面有一些重要的私有变量来维护状态, 注意the transient关键字是让这些变量不能序列化。
  
DSC0001.png

 注意:在Tomcat5中以上的变量时保护的,而在Tomcat 4 中是私有变量。每个变量都有get/set 方法。
  The getSession方法通过传this实例创建一个StandardSessionFacade对象:
  public HttpSession getSession(){
  if(facade == null)
  facade=new StandardSessionFacade(this);
  return facade;
  }
  在Manager中如果一个会话对象没有访问的这个时间段超出了the maxInactiveInternal变量的值那么此会话就过期。能使一个会话对象过期,是通过调用了The Session接口的expire方法。下面就给出在Tomcat 4中,在StanardSession类实现了这个接口的方法:

public void expire(boolean notify) {
// Mark this session as "being expired" if needed
if (expiring)
return;
expiring = true;
setValid(false);
// Remove this session from our manager's active sessions
if (manager != null)
manager.remove(this);
// Unbind any objects associated with this session
String keys[] = keys();
for (int i = 0; i < keys.length; i++)
removeAttribute(keys, notify);
// Notify interested session event listeners
if (notify) {
fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null);
}
// Notify interested application event listeners
// FIXME - Assumes we call listeners in reverse order
Context context = (Context) manager.getContainer();
Object listeners[] = context.getApplicationListeners();
if (notify && (listeners != null)) {
HttpSessionEvent event =
new HttpSessionEvent(getSession());
for (int i = 0; i < listeners.length; i++) {
int j = (listeners.length - 1) - i;
if (!(listeners[j] instanceof HttpSessionListener))
continue;
HttpSessionListener listener =
(HttpSessionListener) listeners[j];
try {
fireContainerEvent(context,
"beforeSessionDestroyed",
listener);
listener.sessionDestroyed(event);
fireContainerEvent(context,
"afterSessionDestroyed",
listener);
} catch (Throwable t) {
try {
fireContainerEvent(context,
"afterSessionDestroyed",
listener);
} catch (Exception e) {
;
}
// FIXME - should we do anything besides log these?
log(sm.getString("standardSession.sessionEvent"), t);
}
}
}
// We have completed expire of this session
expiring = false;
if ((manager != null) && (manager instanceof ManagerBase)) {
recycle();
}
}
   该方法的处理过程包括:设置内部变量,从Manager中移除The Session实例,触发一些事件。
  五、The StandardSessionFacade类
  为了传一个session对象给Servlet,Catalina实例化The StandardSession类并封装这个实例,在把它传给Servlet.  然而,传给Servlet的是一个StandardSessionFacade实例( 该类仅仅实现了javax.servlet.http.HttpSession接口 ),那为什么要这么做呢?答案是:为了让servlet程序设计者编程时,无法让HttpSession对象向上转型为StandardSession,这是因为仅仅让程序设计者访问公共的方法,而一些私有的方法不暴露给程序设计者。
  六、Manager
  一个Manager管理会话对象,例如:它能创建会话对象,也能销毁会话对象。一个Manager由org.apache.catalina.Manager接口代表。在Catalina中,the org.apache.catalina.session包中,包含了ManagerBase类(它实现了公有的功能)。The ManagerBase有两个直接的子类:StandardManager和PersistentManagerBase.
  当容器运行时,The StandardManager在内存中存储会话对象。然而,如果停止,那么StandardManager把当前内存中所有的会话对象存储在一个文件中,当又重新启动时,又会重新加载这些会话对象。
  PersistentManagerBase是manager的一个基本组件(它在二级存储中存储了会话对象),该类也有两个子类: PersistentManager和DistributedManager(给类仅仅在Tomcat 4中能够使用)。下面是UML类图,关于The Manager接口和其实现的类:
  
DSC0002.png
  七、The Manager 接口
  The Manager接口代表了一个Manager组件,下面是其接口的方法:

package org.apache.catalina;

import java.beans.PropertyChangeListener;
import java.io.IOException;
public interface Manager {
public Container getContainer();

/**
* Set the Container with which this Manager is associated.
*
* @param container The newly associated Container
*/
public void setContainer(Container container);

/**
* Return the DefaultContext with which this Manager is associated.
*/
public DefaultContext getDefaultContext();

/**
* Set the DefaultContext with which this Manager is associated.
*
* @param defaultContext The newly associated DefaultContext
*/
public void setDefaultContext(DefaultContext defaultContext);

/**
* Return the distributable flag for the sessions supported by
* this Manager.
*/
public boolean getDistributable();

/**
* Set the distributable flag for the sessions supported by this
* Manager.  If this flag is set, all user data objects added to
* sessions associated with this manager must implement Serializable.
*
* @param distributable The new distributable flag
*/
public void setDistributable(boolean distributable);

/**
* Return descriptive information about this Manager implementation and
* the corresponding version number, in the format
* <code>&lt;description&gt;/&lt;version&gt;</code>.
*/
public String getInfo();

/**
* Return the default maximum inactive interval (in seconds)
* for Sessions created by this Manager.
*/
public int getMaxInactiveInterval();

/**
* Set the default maximum inactive interval (in seconds)
* for Sessions created by this Manager.
*
* @param interval The new default value
*/
public void setMaxInactiveInterval(int interval);

// --------------------------------------------------------- Public Methods

/**
* Add this Session to the set of active Sessions for this Manager.
*
* @param session Session to be added
*/
public void add(Session session);

/**
* Add a property change listener to this component.
*
* @param listener The listener to add
*/
public void addPropertyChangeListener(PropertyChangeListener listener);

/**
* Construct and return a new session object, based on the default
* settings specified by this Manager's properties.  The session
* id will be assigned by this method, and available via the getId()
* method of the returned session.  If a new session cannot be created
* for any reason, return <code>null</code>.
*
* @exception IllegalStateException if a new session cannot be
*  instantiated for any reason
*/
public Session createSession();

/**
* Return the active Session, associated with this Manager, with the
* specified session id (if any); otherwise return <code>null</code>.
*
* @param id The session id for the session to be returned
*
* @exception IllegalStateException if a new session cannot be
*  instantiated for any reason
* @exception IOException if an input/output error occurs while
*  processing this request
*/
public Session findSession(String id) throws IOException;

/**
* Return the set of active Sessions associated with this Manager.
* If this Manager has no active Sessions, a zero-length array is returned.
*/
public Session[] findSessions();

/**
* Load any currently active sessions that were previously unloaded
* to the appropriate persistence mechanism, if any.  If persistence is not
* supported, this method returns without doing anything.
*
* @exception ClassNotFoundException if a serialized class cannot be
*  found during the reload
* @exception IOException if an input/output error occurs
*/
public void load() throws ClassNotFoundException, IOException;

/**
* Remove this Session from the active Sessions for this Manager.
*
* @param session Session to be removed
*/
public void remove(Session session);

/**
* Remove a property change listener from this component.
*
* @param listener The listener to remove
*/
public void removePropertyChangeListener(PropertyChangeListener listener);
public void unload() throws IOException;
}

   首先,The Manager接口有getContainer和setContainer方法目的是一个实现Manager接口类与一个Context相关联。The CreateSession方法就是创建一个会话对象。The add方法把会话实例添加到会花池中,The remove方法是把会话对象从会话池中移除掉。The getMaxInactiveInternal和setMaxInactiveInternal方法返回(and specifices the number of sends the Manager will wait for the user associated with a session to  come back before destorying the session).这里不会翻译。。。悲剧。。。

  最后,the load和upload方法支持把可持续化的会话存储在第二个存储空间中(这个是有PersistManager中支持可持续化类型机制),The upload方法保存了当前激活的会话对象(也就是第一存储空间中该类时standardManger),并且the Load方法也支持把存储在第二存储空间的会话对象引入到内存中。
  八、The ManagerBase类
  The ManagerBase类是一个抽象类( 它的父类是Manager类 )。这个类为它的孩子提供公共的功能,此外,ManagerBase有一个createSession方法创建一个会话对象。每个session有一个唯一的标志符,这个标识符是由保护方法generateSessionId方法返回。
  注意:一个激活的session是一个有效的会话对象,而没有过期。
  一个特定的context下的Manager的实例管理整个激活的session. 这些激活的session存储在一个叫sessions的HashMap中:
  protected HashMap sessions= new HashMap();
  The add方法把一个session对象添加到sessions  HashMap中,这个方法请看下面的代码:
  public void add(Session session){
  synchronized(sessions){
  sessions.put(session.getId(),session);
  }
  }
  The remove方法从HashMap中移除一个会话对象,下面是其方法代码:
  public void remove(Session session){
  synchronized(sessions){
  sessions.remove(session.getId());
  }
  }
  The findSession没有参数的方法,是返回全部激活的会话( 来自存放在HashMap中的全部激活的session,他转换成数组
  )。The findSession有参数的方法(session标志符作为参数),它返回一个该标志符对应的一个会话实例。这些重载的方法请看下面代码:

public Session[] findSessions() {
Session results[] = null;
synchronized (sessions) {
results = new Session[sessions.size()];
results = (Session[]) sessions.values().toArray(results);
}
return (results);
}
public Session findSession(String id) throws IOException {
if (id == null)
return (null);
synchronized (sessions) {
Session session = (Session) sessions.get(id);
return (session);
}
}
   九、StandardManager
  The StandardManager类是Manager接口的一个标准实现,在内存中存储了整个会话对象,它也同时实现了The Lifecycle接口(请看在第六章、Lifecycle),这样就可以用start和stop来控制组件。实现stop方法中调用了upload方法(就是序列化有效的session实例到文件中,文件名字叫做SESSIONS.ser)。The SESSIONS.ser文件能够在CATALINA_HOME目录下找到。比如,在Tomcat 4和 Tomcat 5 中,如果 你运行本应用程序的代码,你就会发现在CATALINA_HOME/work/Standalone/localhost/examples目录下能找到SESSIONS.ser文件。当StandardManager有重新启动时,这些会话对象通过调用the Load方法全部读到内存中。
  A Manager也负责销毁不再有效的会话对象。在Tomcat 4 中的StandardManager中,这样的实现,它是用了一个专门的线程来实现销毁会话对象。由于这个原因,StandardManager实现了 java.lang.Runnable, 请看下面实现run的方法:

  public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processExpires();
}
}
  The threadSleep方法调用了threadSleep(里面主要是Thread.sleep(checkInterval * 1000L)),the checkInterval变量是以秒为单位,默认值是60秒,也可以通过调用setCheckInterval方法改变这个值。
  The processExpire方法: 循环遍历由StandardManager管理的整个会话对象,每个Session对象实例把lastAccessedTime变量与当前时间相比较。如果当前时间减去 lastAccessedTime的值大于 maxInactiveInternal,那么该方法就会调用The session接口中的expire方法使得会话该对象过期。The maxInactiveInternal的值通过调用setMaxInactiveInternal方法来改变这个值。在StandardManager中的maxInactiveInternal变量默认值是 60. 呵呵呵。。不要愚蠢的认为maxInactiveInternal值是在Tomcat部署的时候设置的。同时,The setContainer方法:它调用了the org.apache.catalina.core.ContainerBase类中的setManager方法(你一直都要调用setManager方法目的是context和Manager相关联),来重写这个值。下面是setContainer方法中的一些代码:
  setMaxInactiveInternal((( Context ) this.container ).getSessionTimout() * 60 );
  注意:the sessionTimeOut变量的默认值在org.apache.catalina.core.StandardContext类是30秒。在Tomcat 5中,The StandradManager类没有实现 java.lang.Runnable接口。在Tomcat 5 中的StandardManager对象中的The processExpires方法直接调用了the backgroundprocess方法,但是Tomcat 4不是这样做的。
  public  void   backgroundProcess(){
  processExpires();
  }
  在 StandardManager中的backgroundProcess方法是通过the org.apache.catalina.core.StandardContext实例(the Container和manager相关联)被调用的。StandardContext周期的调用了backgroundProcess方法这个将会在第十二章进行讨论。
  十、PersistentManagerBase
  The PersistentManagerBase类是整个可持久化管理的父类。StandardManager和 persistent manager主要的区别就是后者存在一个store,一个 store代表可管理session对象的第二级存储,The PersistentManagerBase类使用了一个私有引用对象(叫做store).
  private Store store=null;
  在一个可持久化管理中,整个session对象既能备份有能迁移。当一个会话对象备份时,The session 对象就会拷贝到一个store中,原始的那个还是会继续停留在内存中。因此,如果服务器崩溃了,激活的会话对象能够在the store中恢复过来。当一个session对象被迁移时,它就会被迁移到the store中,那为什么要这么做呢?这是因为,激活的会话对象的数目已经超出了一个特定的值或者The session对象已经停留在内存中太久并且从来没有用到过它。迁移的目的就是保存内存中的会话对象。注意激活的对象就是停留在内存中的对象。
  在Tomcat 4 中PersistentManagerBase类实现了java.lang.Runnable接口,目的是使用一个独立的线程周期的备份和迁移激活的会话对象,下面是实现run方法的代码:

运维网声明 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-338070-1-1.html 上篇帖子: Tomcat 7 新特性学习之一 下篇帖子: Web Service框架XFire与Tomcat集成(1)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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