您的位置:首页 > 运维架构 > Tomcat

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

2013-03-18 14:15 573 查看
通常,每一个部署在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代表数据库存储。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  tomcat