《How Tomcat Works》读书笔记(九)Session Management

      通常,每一个部署在Tomcat上的Web项目(Context)都会有一个会话管理器与之关联。负责会话的创建、获取、更新、删除。
下面来个简单的示例:

Manager manager = null;
if (context != null) manager = context.getManager();//从Context中获取Manager
if (manager == null) return (null);
if (requestedSessionId != null) {
    try {
        //manager根据sessionid获取session
        session = manager.findSession(requestedSessionId);
    }catch (IOException e) {
        session = null;
    }
    if ((session != null) && !session.isValid()) session = null;
    if (session != null) { return (session.getSession()); }
}
if (!create) return (null);
...
session = manager.createSession();//manager创建session
if (session != null)
    return (session.getSession());
else
    return (null);
}

Session Manager默认在内存中存放Session对象,它还可以持久化Session对象到文件或数据库中。本文后面会介绍。

在servlet编程中,一个session对象由javax.servlet.http.HttpSession接口表示,在一个Manager中,一个session对象由org.apache.catalina.Session接口表示。
StandardSession实现了javax.servlet.http.HttpSession和org.apache.catalina.Session接口。
我们通过javax.servlet.http.HttpServletRequest的HttpSession getSession()方法可以获取当前会话。
出于安全考虑,Manager传递给我们的是一个叫做StandardSessionFacade类(只实现了javax.servlet.http.HttpSession接口),而不是StandardSession,这样做可以有效防止servlet开发者调用他不该看到的定义在org.apache.catalina.Session接口中的方法。
他们的关系如图:

Session接口
StandardSessionFacade是StandardSession作为HttpSession时的Facade。
Manager也和一个Facade一起工作:Session

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();//id作为一个session对象在Context中的唯一标示
    public void setId(String id);
    public String getInfo();
    public long getLastAccessedTime();//Manager调用这个方法判断session是否过期、非法
    public Manager getManager();//一个session对象通常包含在一个Manager中
    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);
}
StandardSession 
//实现java.lang.Serializable接口使session可序列化,反序列化
public class StandardSession implements Serializable{
    //构造方法,强制与一个Manager关联
    public StandardSession(Manager manager){
        this.manager = manager;
    }
    //session属性
    private HashMap attributes = new HashMap();
    private transient String authType = null;//transient表示不进行序列化的属性
    private long creationTime = 0L;
    private transient boolean expiring = false;
    private transient StandardSessionFacade facade = null;
    private String id = null;
    private long lastAccessedTime = creationTime;
    private transient ArrayList listeners = new ArrayList();
    private Manager manager = null;
    private int maxInactiveInterval = -1;
    private boolean isNew = false;
    private boolean isValid = false;
    private long thisAccessedTime = creationTime;
    ......
    public HttpSession getSession() {
        if (facade == null)
            facade = new StandardSessionFacade(this);//返回一个Facade
        return (facade);
    }
}
Session过期
Manager中会定义一个maxInactiveInterval变量,当一个session超过 maxInactiveInterval时间没有被访问就视为过期。

Manager维护的一个后台线程会调用session的expire方法

public void expire(boolean notify) {
    if (expiring) return;//已过期标示
    expiring = true;
    setValid(false);
    if (manager != null)
        manager.remove(this);//从Manager管理的活跃会话中删除
    String keys [] = keys();
    for(int i = 0; i < keys.length; i++)
        removeAttribute(keys[i], notify);//把与session关联的对象解绑
    if (notify) {//通知对SESSION_DESTROYED_EVENT感兴趣的监听器
        fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null);
    }
    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}{
                    ;
                }
                log(sm.getString("standardSession.sessionEvent"), t);
            } 
        }
    }
    expiring = false;
    if ((manager != null) && (manager instanceof ManagerBase)){
        recycle();
    }
}

Manger
Manager负责管理session,org.apache.catalina.session包中有一个抽象类ManagerBase,它有两个子类StandardManager、PersistentManagerBase。
StandardManager运行时:把session存放在内存中;停止时:把session存放在一个文件中,当再一次启动时,从文件中把session恢复到内存中来。
PersistentManagerBase:把session存放在二级存储中,如硬盘。它有两个子类PersistentManager、DistributedManager。

Manager接口:

public interface Manager {
    public Container getContainer();
    public void setContainer(Container container);
    public DefaultContext getDefaultContext();
    public void setDefaultContext(DefaultContext defaultContext);
    public boolean getDistributable();
    public void setDistributable(boolean distributable);
    public String getInfo();
    public int getMaxInactiveInterval();
    public void setMaxInactiveInterval(int interval);
    public void add(Session session);//加入到session pool中
    public void addPropertyChangeListener(PropertyChangeListener listener);
    public Session createSession();
    public Session findSession(String id) throws IOException;public Session[] findSessions();
    public void load() throws ClassNotFoundException, IOException;//从一个特定存储中加载
    public void remove(Session session);
    public void removePropertyChangeListener(PropertyChangeListener listener);
    public void unload() throws IOException;//存放到特定存储中
}
ManagerBase:
public abstract class ManagerBase implements Manager {
    ......
    protected HashMap sessions = new HashMap();
    public void add(Session session) {
        synchronized (sessions) {
            sessions.put(session.getId(), session);
        }
    }
    public void remove(Session session) {
        synchronized (sessions) {
            sessions.remove(session.getId());
        }
    }
    public Session findSession(String id) throws IOException {
        if (id == null)
            return (null);
        synchronized (sessions) {
            Session session = (Session) sessions.get(id);
            return (session);
        }
    }
    ......
}
PersistentManagerBase
PersistentManagerBase用一个 Store代表存放session对象的二级存储。
private Store store = null;
session对象可以 备份(back up)到Store,并且可以从Store中 换出 (swap out)
他们两个的区别在于:back up是内存、Store各有一份;swap out:仅Store里有一份。 

DistributedManager
DistributedManager在集群环境(两台以上机器)中使用。在集群中会有一个个结点(Node),不同的结点可以在一台机器中,也可以在不同机器中。在集群环境中,每一个结点必须拥有一个DistributedManager实例作为结点的Manager,负责Session同步(复制)。
当一个Session对象建立/销毁的时候,它所在的结点就要通知其他结点进行复制/销毁。
Tomcat提供了两个类来收发通知:ClusterSender发送通知,ClusterReceiver接收通知。

public Session createSession() {
    Session session = super.createSession();
    ObjectOutputStream oos = null;
    ByteArrayOutputStream bos = null;
    ByteArraylnputStream bis = null;
    try {
        bos = new ByteArrayOutputStream();
        oos = new ObjectOutputStream(new BufferedOutputStream(bos));
        ((StandardSession)session).writeObjectData(oos);
        oos.close();
        byte[] obs = bos.toByteArray();
        clusterSender.send(obs);
        if(debug > 0) log("Replicating Session: "+session.getId());
    } catch (IOException e) {
        log("An error occurred when replicating Session: " + session.getId());
    }
    retun (session);
}
同时,DistribubedManager实现了java.lang.Runnable来确保有一个单独的线程接收其它结点发来的消息。
public void run() {
    while (!threadDone) {
        threadSleep();
        processClusterReceiver();
        processExpires();
        processPersistenceChecks();
    }
}
Store接口
public interface Store {
    public String getInfo();
    public Manager getManager();
    public void setManager(Manager manager);
    public int getSize() throws IOException;
    public void addPropertyChangeListener(PropertyChangeListener listener);
    public String[] keys() throws IOException;
    public Session load(String id)throws ClassNotFoundException, IOException;
    public void remove(String id) throws IOException;
    public void clear() throws IOException;
    pubiic void removePropertyChangeListener(PropertyChangeListener listener);
    public void save(Session session) throws IOException;
}

StoreBase是一个抽象类,为两个子类提供了一些通用的方法,但是没有实现save和load方法,因为他们依赖于不同类型的存储。
FileStore代表文件存储:使用java.io.ObjectOutputStream来序列化session,java.io.ObjectInputStream来反序列化session。
JDBCStore代表数据库存储。



转载于:https://my.oschina.net/itjava/blog/114443

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值