《How Tomcat Works》讀書筆記(九)Session Management

      一般,每個部署在Tomcat上的Web項目(Context)都會有一個會話管理器與之關聯。負責會話的建立、獲取、更新、刪除。
下面來個簡單的示例: java

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接口中的方法
他們的關係如圖:

apache

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接口:
session

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接收通知。 this

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表明數據庫存儲。
spa

相關文章
相關標籤/搜索