Coder 爱翻译 How Tomcat Works 第九章 第二部分

[size=large]The ManagerBase Class[/size]

ManagerBase类是一个从所有Manager抽取出来到的抽象类。这个类给它的子类提供了常见的方法。ManagerBase有createSession方法来创建一个Session对象。每一个session都有一个唯一的标识符,ManagerBase类的protected方法generateSessionId返回一个唯一的标识符。

注意:一个活动状态下的session是一个session仍然是valid对象,而不是expired的。这些活动的session被存储在一个叫做sessions的HashMap中的:

protected HashMap sessions = new HashMap();


add方法把一个Session对象添加到sessions中:

public void add(Session session) {
synchronized (sessions) {
sessions.put(session.getId(), session);
}
}

remove方法从sessions中移除一个Session对象。

public void remove(Session session) {
synchronized (sessions) {
sessions.remove(session.getId());
}
}

没有参数的findSession方法返回在sessions中的活动的所有session对象的实例数组。findSession方法接收一个session标识符作为一个参数返回给出的标识符的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);
}
}

[size=large]StandardManager[/size]

StandardManager类是Manager的标准实现,它用来把Session对象存储到内存中。它实现了Lifecycle接口,所以它可以被启动和停止。stop方法的实现调用unload方法,为每一个context把合法的Session实例以串行化方式存入一个叫做SESSION.ser的文件中。SESSION.ser文件可以在CATALINA_HOME目录下找到。当StandardManager再次启动的时候,使用load方法把这些Session对象被重新加载到内存中去。

一个manager也负责销毁那些不再合法的session对象。Tomcat 4中的StandardManager,它拥有一个指定的线程来实现。所以,StandardManager它实现了java.lang.Runnable接口。

public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processExpires();
}
}

ThreadSleep方法让这个线程处于睡眠等待checkInterval变量指定的秒数。默认是60秒。你可以调用setCheckInterval方法来设置这个变量值。

ProcessExpire方法循环地把所有的被StandardManager管理的Session对象中的每一个Session对象的lastAccessedTime值和当前的时间值比较。如果这两个时间的差值超过了maxInactiveInterval值,这个方法就调用Session接口的expire方法把这个Session实例设置为超时。maxInactiveInterval值可以通过调用setMaxInactiveInterval方法来改变。StandardManager下默认的maxInactiveInterval变量值是60。不要简单地认为这个值是在Tomcat部署时用到的值,setContainer方法org.apache.catalina.core.ContainerBase 类的setManager方法被调用,重写这个值:

setMaxInactiveInterval(((Context)
this.container).getSessionTimeout()*60 );

注意:在org.apache.catalina.core.StandardContext类的sessionTimeOut变量的默认值是30。

在Tomcat 5中,StandardManager类没有实现java.lang.Runnable.。在Tomcat 5中的StandardManager中的processExpires方法被backgroundprocess方法直接调用。

public void backgroundProcess() {
processExpires();
}

在StandardManager中的backgroundProcess方法通过org.apache.catalina.core.StandardContext实例的backgroundProcess方法调用,这个容器和这个manager相关联。每隔一段时间StandardContext调用它的backgroundProcess方法。

[size=large]PersistentManagerBase[/size]

PersistentManagerBase类是所有持久化管理器的父类。StandardManager和一个持久化manager主要的不同是:后面有一个store。一个store代表一个辅助存储器用来管理session对象。PersistentManagerBase类使用一个private的叫做store的对象引用。

private Store store = null;

在一个持久化manager中,session对象可以被备份和被清理。当一个session对象被备份,这个session对象被复制到一个store中,原来的session对象还在内存中。如果服务器down掉了,活动的session对象可以在store中重新获取。当一个session对象它被因为活动的session对象的数量超过了指定的数量或者session对象已经空闲了很长一段时间,清理的目的是为了节省内存空间。

在Tomcat 4中PersistentManagerBase实现了java.lang.Runnable来持有一个单独的线程来定期备份和清理活动的session。

public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processExpires();
processPersistenceChecks();
}
}

processExpired方法,就像在StandardManager中的一样,是检查session对象是否超时。processPersistenceChecks方法调用了其他三个方法:

public void processPersistenceChecks() {
processMaxIdleSwaps();
processMaxActiveSwaps();
processMaxIdleBackups();
}

在Tomcat 5中,PersistentManagerBase没有实现java.lang.Runnable接口。备份和清理工作是由backgroundProcess manager处理的。它由与它相关联的StandardContext实例定期地调用。
[size=x-large]Swap Out[/size]

PersistentManagerBase类采用一系列规则来清理session对象。一个session对象在超过最大活动session的maxActiveSessions变量值或者session已经空闲了一段很长时间时被清理。

这种有太多的session对象的情况下,一个PersistentManagerBase实例简单地清理任何一个session知道活动session对象的值等于maxActiveSessions。

在一个session对象空闲了很长一段时间这种情况,PersistentManagerBase类使用两个变量来决定是否这个session对象被清理:minIdleSwap和maxIdleSwap。一个session对象在它的lastAccessedTime超过minIdleSwap和maxIdleSwap时,会被清理掉。为了保护被清理掉,你可以设置maxIdleSwap为负数。

因为一个活动的session可以被清理,它可以被重新加载到内存和存储到一个store中。findSession (String id)方法首先在内存中寻找Session实例,如果没有,就在store中搜索。下面是PersistentManagerBase类方法的实现。

public Session findSession(String id) throws IOException {
Session session = super.findSession(id);
if (session != null)
return (session);
// not found in memory, see if the Session is in the Store
session = swapIn(id); // swapIn returns an active session in the Store
return (session);
}

[size=large]Back-up[/size]

不是所有活动的session对象会被备份。一个PersistentManagerBase实例只会备份空闲时间比maxIdleBackup的值大的session对象。processMaxIdleBackups方法负责备份session对象。

[size=large]PersistentManager[/size]

PersistentManager继承了PersistentManagerBase:

Listing 9.5: The PersistentManager class

package org.apache.catalina.session;
public final class PersistentManager extends PersistentManagerBase {
// The descriptive information about this implementation.
private static final String info = "PersistentManager/1.0";
// The descriptive name of this Manager implementation (for logging).
protected static String name = "PersistentManager";
public String getInfo() {
return (this.info);
}
public String getName() {
return (name);
}
}


[size=large]DistributedManager[/size]

Tomcat 4中提供了DistrubutedManager类。是PersistentManagerBase的一个子类,DistributedManager被用在一个集群环境,有两个或更多节点。一个节点代表一个Tomcat的部署。在一个集群中的节点可以存在于不同的计算机中或在同一台计算机中。在集群环境中,
每一个节点必须使用一个DistributedManager实例作为它的Manager来支持session的复制。这个是DistributedManager的主要工作。

为了达到复制的目的,DistributedManager发送通知给其它节点。不管说么么时候一个session对象被创建或销毁。此外,一个节点必须能够接收到其它节点的通知。一个HTTP请求可以在集群中的任何一个节点得到服务。

发送和接收其它节点的DistributedManager实例的通知,Catalina提供org.apache.catalina.cluster包的类。ClusterSender类用来发送通知给其它节点,ClusterReceiver类用来接收通知。

DistrbutedManager的createSession方法必须创建一个session对象存储到当前实例,使用ClusterSender实例发送通知给其它节点。

Listing 9.6: The createSession method

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);
}

首先createSession方法调用父类的createSession方法来创建一个session对象。它发送session对象作为一个字节数组。

DistribubedManager类也实现了java.lang.Runnable,它有一个独立的线程把session对象超时,接收其它节点来的通知。

public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processClusterReceiver();
processExpires();
processPersistenceChecks();
}
}


[size=large]Stores[/size]

一个store由一个org.apache.catalina.Store接口表示,它是一个提供永久存储session的组件。

Listing 9.7: The Store interface

package org.apache.catalina;
import java.beans.PropertyChangeListener;
import java.io.IOException;

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;
}

Store接口中两个最重要的方法:save和load。save方法存储指定的session对象要一个永久存储器。load方法用给出的指定标识符从存储器中加载session对象。keys方法返回一个字符串数组,这个数组包含所有的session标识符。

类图:
[img]http://dl.iteye.com/upload/attachment/403877/06f7bea7-e06d-36f2-8e63-a272d13e0644.jpg[/img]

[size=large]StoreBase[/size]

StoreBase类是一个抽象类,它为它的两个子类提供了常用的方法:FileStore和JDBCStore。StoreBase类没有实现Store接口的save和load方法,因为这个方法的实现依赖存储类型来存储session。

Tomcat 4中的StoreBase类持有一个单独的线程来定期地检查超时的session和移除活动session集合中超时的session。

public void run() {
// Loop until the termination semaphore is set
while (!threadDone) {
threadSleep();
processExpires();
}
}

processExpires方法获取所有的活动的session和检查每一个lastAccessedTime的值,移除session对象空闲了很长时间。

Listing 9.7: the processExpires method

protected void processExpires() {
long timeNow = System.currentTimeMillis();
String[] keys = null;
if (!started)
return;
try {
keys = keys();
}
catch (IOException e) {
log (e.toString());
e.printStackTrace();
return;
}
for (int i = 0; i < keys.length; i++) {
try {
StandardSession session = (StandardSession) load(keys[i]);
if (!session.isValid())
continue;
int maxInactiveInterval = session.getMaxInactiveInterval();
if (maxInactiveInterval < 0)
continue;
int timeIdle = // Truncate, do not round up
(int) ((timeNow - session.getLastAccessedTime()) / 1000L);
if (timeIdle >= maxInactiveInterval) {
if ( ( (PersistentManagerBase) manager).isLoaded( keys[i] )) {
// recycle old backup session
session.recycle();
}
else {
// expire swapped out session
session.expire();
}
remove(session.getId());
}
}
catch (IOException e) {
log (e.toString());
e.printStackTrace();

}
catch (ClassNotFoundException e) {
log (e.toString());
e.printStackTrace();
}
}
}


在Tomcat 5中没有特别的线程来调用processExpires方法。这个方法定期地被与它相关联的PersistentManagerBase实例的backgroundProcess方法调用。

[size=large]FileStore[/size]

FileStore方法把session对象存储到文件中。文件是以session对象的标识符为名字,以.session为后缀名。这个文件位于临时的工作目录中。你可以调用Filestore类的setDirectory方法来更改临时目录。

java.io.ObjectOutputStream类是在sava方法中用来串行化session对象。存储在一个Session实例的所有对象都必须实现java.lang.Serializable。反序列化一个session对象是在load方法,java.io.ObjectInputStream类调用。

[size=large]JDBCStore[/size]

JDBCStore类存储session对象到一个数据库中,是通过JDBC来传输的,使用JDBCStore你需要设置驱动名字和链接URL,通过调用各自的setDriverName和setConnectionURL方法。

[size=large]The Application[/size]

本章的应用程序,使用默认连接器,它有一个context作为它的主容器。
这个应用程序有两个包:ex09.pyrmont.core和ex09.pyrmont.startup,在Catalina中使用不同的类。在ex09.pyrmont.core包下有四个类:SimpleContextConfig, SimplePipeline, SimpleWrapper和SimpleWrapperValve.
ex09.pyrmont.startup包下有一个类:Bootstrap。


[size=large]The Bootstrap Class[/size]

main方法开始是设置catalina.base系统属性和初始化默认连接器。

System.setProperty("catalina.base",
System.getProperty("user.dir"));
Connector connector = new HttpConnector();

SessionServlet,它创建一个wrapper,命名为: wrapper1.

Wrapper wrapper1 = new SimpleWrapper();
wrapper1.setName("Session");
wrapper1.setServletclass("SessionServlet");

然后创建一个StandardContext对象,设置它的路径和docBase属性,添加wrapper到context。

Context context = new StandardContext();
context.setPath("/myApp");
context.setDocBase("myApp");
context.addChild(wrapper1);

接下来start方法添加一个servlet映射。这个映射和第八章的不同。/Session,我们使用myApp/Session作为。

context.addServletMapping("/myApp/Session", "Session")

请求的url:http://localhost:8080/myApp/Session.

context需要一个监听器和一个加载器。

LifecycleListener listener = new SimpleContextConfig();
((Lifecycle) context).addLifecycleListener(listener);
// here is our loader
Loader loader = new WebappLoader();
// associate the loader with the Context
context.setLoader(loader);
connector.setContainer (context);

现在我们使用一个StandardManager实例,并把它传递给context。

Manager manager = new StandardManager();
context.setManager(manager);


最后,我们初始化和启动链接诶器和启动context。

connector.initialize();
((Lifecycle) connector).start();
((Lifecycle) context).start();


[size=large]The SimpleWrapperValve Class[/size]


Listing 9.8: The invoke method of the SimpleWrapperValve class

public void invoke(Request request, Response response,
ValveContext valveContext) throws IOException, ServletException {

SimpleWrapper wrapper = (SimpleWrapper) getContainer();
ServletRequest sreq = request.getRequest();
ServletResponse sres = response.getResponse();
Servlet servlet = null;
HttpServletRequest hreq = null;
if (sreq instanceof HttpServletRequest)
hreq = (HttpServletRequest) sreq;
HttpServletResponse hres = null;
if (sres instanceof HttpServletResponse)
hres = (HttpServletResponse) sres;
// pass the Context to the Request object so that
// the Request object can call the Manager
Context context = (context) wrapper.getparent();
request.setcontext(context);

// Allocate a servlet instance to process this request
try {
servlet = wrapper.allocate();
if (hres!=null && hreq!=null) {
servlet.service(hreq, hres);
}
else {
servlet.service(sreq, sres);
}
}
catch (ServletException e) {
}
}


你要访问wrapper,你可以调用Container接口的getParent方法获取这个context。注意:wrapper被添加到context中了。一旦你有一个context,你可以调用Request接口的setContext方法。

org.apache.catalina.connector.HttpRequestBase类的private的doGetSession方法调用Context接口的getManager方法来获取manager。

// Return the requested session if it exists and is valid
Manager manager = null;
if (context != null)
manager = context.getManager();

当有了一个manager,就可以直接获取一个session对象或创建一个新的session对象。

总结:本章讨论manager,它是在session管理里面的一个管理session的组件。组件类型和一个manager怎么持久化session对象到store中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值