1、从server.xml看tomcat的结构
tomcat的结构可以从server.xml文件看出一二,例如server包含service,而service包含engine,engine中包含host。 去除监听器、用户登录等配置,server.xml如下:
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Context path = "/word" docBase = "G:\java\web\hello" />
</Host>
</Engine>
</Service>
</Server>
转换成图如下图所示:
各个结构含义如下:
server:代表一个tomcat实例,同时所有的配置都应该在server标签之间,经过解析后,server标签会生成对应的org.apache.catalina.Server对象。
service:代表一个服务,同样解析后会生成对应的org.apache.catalina.Service。
connector:连接器,作用是监听端口,接收socket请求,并交由容器处理。较老版本的tomcat会生成org.apache.catalina.Connector对象,这是一个接口,新版本的会生成对应的org.apache.catalina.connector.Connector对象,这是一个类。如上图,一个Service是可以有多个connector的,只要监听的端口不同。
容器:
Engine:引擎,一个引擎可以包含多个Host,解析后会生成对应的org.apache.catalina.Engine对象。
Host:主机,例如访问的localhost就是tomcat的默认配置。解析后会生成对应的org.apache.catalina.Host对象。
Context:应用上下文,具体部署的服务。解析后会生成对应的org.apache.catalina.Conext对象。
其他组件:
Valve:阈,在容器处理请求前,会由一个叫做Pipeline的组件调用Valve,容器可以有多个Valve,在tomcat4.x和5.x,是使用Valve数组的形式来循环遍历调用的,但tomcat7之后是基于链的形式调用的。 解析后会生成相应的org.apache.catalina.Valve对象。
Listener:监听器,tomcat在启动、停止等生命周期的相应操作。解析后会生成org.apache.catalina.LifecycleListener对象。
以上就是基本可以从server.xml文件中读出的配置,但还有一些其他组件配置,例如Host中的Realm。
2、从源码看tomcat的结构
2.1、 Server组件
public interface Server extends Lifecycle {
//接口方法略
}
编写Servlet应该知道,Servlet有初始化、销毁、服务的生命周期。在tomcat中,用org.apache.catalina.Lifecycle来表示生命周期,用org.apache.catalina.LifecycleListener接口表示生命周期监听器,用org.apache.catalina.LifecycleEvent表示生命周期的各个事件,比如启动、停止、销毁、启动前等事件。同时tomcat还添加了许多XXSuport类来简化组件处理监听器事件和生命周期事件。
/**
* The set of Services associated with this Server.
*/
private Service services[] = new Service[0];
private final Object servicesLock = new Object();
添加service源码:
@Override
public void addService(Service service) {
service.setServer(this);
synchronized (servicesLock) {
Service results[] = new Service[services.length + 1];
System.arraycopy(services, 0, results, 0, services.length);
results[services.length] = service;
services = results;
if (getState().isAvailable()) {
try {
service.start();
} catch (LifecycleException e) {
// Ignore
}
}
// Report this property change to interested listeners
support.firePropertyChange("service", null, service);
}
}
虽然初始定义services的length为1,但从addService方法可以看出,每次调用该方法都会将数组扩容到原长度 + 1,并将原数组数据复制过去。
@Override
protected void startInternal() throws LifecycleException {
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
globalNamingResources.start();
// Start our defined Services
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
services[i].start();
}
}
}
2.2 、Service组件
/**
* The Container associated with this Service. (In the case of the
* org.apache.catalina.startup.Embedded subclass, this holds the most
* recently added Engine.)
*/
protected Container container = null;
@Override
public void setContainer(Container container) {
Container oldContainer = this.container;
if ((oldContainer != null) && (oldContainer instanceof Engine))
((Engine) oldContainer).setService(null);
this.container = container;
if ((this.container != null) && (this.container instanceof Engine))
((Engine) this.container).setService(this);
if (getState().isAvailable() && (this.container != null)) {
try {
this.container.start();
} catch (LifecycleException e) {
// Ignore
}
}
if (getState().isAvailable() && (oldContainer != null)) {
try {
oldContainer.stop();
} catch (LifecycleException e) {
// Ignore
}
}
// Report this property change to interested listeners
support.firePropertyChange("container", oldContainer, this.container);
}
它根本无法添加除Engine类型外的其它容器类型。
/**
* The set of Connectors associated with this Service.
*/
protected Connector connectors[] = new Connector[0];
private final Object connectorsLock = new Object();
所以到这儿Service的内部结构很清楚了:一个Engine + 多个Connector。
2.3、Engine容器
/**
* The child Containers belonging to this Container, keyed by name.
*/
protected HashMap<String, Container> children =
new HashMap<String, Container>();
但Engine中只能添加Host类型,原因在于StandardEngine覆盖了ContainerBase的addChild方法:
/**
* Add a child Container, only if the proposed child is an implementation
* of Host.
*
* @param child Child container to be added
*/
@Override
public void addChild(Container child) {
if (!(child instanceof Host))
throw new IllegalArgumentException
(sm.getString("standardEngine.notHost"));
super.addChild(child);
}
2.4、Host容器
@Override
public void addChild(Container child) {
child.addLifecycleListener(new MemoryLeakTrackingListener());
if (!(child instanceof Context))
throw new IllegalArgumentException
(sm.getString("standardHost.notContext"));
super.addChild(child);
}
所以Host的结构为:多个Context容器。除此之外,也可以改变某些属性来改变tomcat的行为,例如autoDeploy=true改为false,则tomcat不会自动部署webapps下的war,但还是会解压war包。如果更改reloadable="true",tomcat则会在源代码改变的情况下重启tomcat。
2.5、Context容器
一个Context容器对应一个应用,Context容器的标准实现类为StandardContext类,Context容器只能添加Wrapper容器作为子容器。addChild方法代码与Host中的代码没有太大不同,所以这儿就不贴代码了。2.6、Wrapper容器
@Override
public Servlet allocate() throws ServletException {
// If we are currently unloading this servlet, throw an exception
if (unloading) {
throw new ServletException(sm.getString("standardWrapper.unloading", getName()));
}
boolean newInstance = false;
// If not SingleThreadedModel, return the same instance every time
if (!singleThreadModel) {
// Load and initialize our instance if necessary
if (instance == null || !instanceInitialized) {
synchronized (this) {
if (instance == null) {
try {
if (log.isDebugEnabled()) {
log.debug("Allocating non-STM instance");
}
// Note: We don't know if the Servlet implements
// SingleThreadModel until we have loaded it.
instance = loadServlet();
newInstance = true;
if (!singleThreadModel) {
// For non-STM, increment here to prevent a race
// condition with unload. Bug 43683, test case
// #3
countAllocated.incrementAndGet();
}
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("standardWrapper.allocate"), e);
}
}
if (!instanceInitialized) {
initServlet(instance);
}
}
}
if (singleThreadModel) {
if (newInstance) {
// Have to do this outside of the sync above to prevent a
// possible deadlock
synchronized (instancePool) {
instancePool.push(instance);
nInstances++;
}
}
} else {
if (log.isTraceEnabled()) {
log.trace(" Returning non-STM instance");
}
// For new instances, count will have been incremented at the
// time of creation
if (!newInstance) {
countAllocated.incrementAndGet();
}
return instance;
}
}
synchronized (instancePool) {
while (countAllocated.get() >= nInstances) {
// Allocate a new instance if possible, or else wait
if (nInstances < maxInstances) {
try {
instancePool.push(loadServlet());
nInstances++;
} catch (ServletException e) {
throw e;
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("standardWrapper.allocate"), e);
}
} else {
try {
instancePool.wait();
} catch (InterruptedException e) {
// Ignore
}
}
}
if (log.isTraceEnabled()) {
log.trace(" Returning allocated STM instance");
}
countAllocated.incrementAndGet();
return instancePool.pop();
}
}
Wrapper内部维护的Servlet实例个数是受到maxInstances变量限制的,默认是20,也就是说,如果同一时刻有超过20个以上的线程向同一个Servlet发起请求,只有最先的20个请求会被处理,其他的请求在其他请求完成处理前只能等待。
2.7、connector组件
2.8、Pipeline组件
/**
* The basic Valve (if any) associated with this Pipeline.
*/
protected Valve basic = null;
/**
* The Container with which this Pipeline is associated.
*/
protected Container container = null;
/**
* The first valve associated with this Pipeline.
*/
protected Valve first = null;
调用getValve()时,如果没有设置first,则会返回basic。
2.9、valve组件
public void invokeNext(Request request, Response response)
throws IOException, ServletException {
int subscript = stage;
stage = stage + 1;
// Invoke the requested Valve for the current request thread
if (subscript < valves.length) {
valves[subscript].invoke(request, response, this);
} else if ((subscript == valves.length) && (basic != null)) {
basic.invoke(request, response, this);
} else {
throw new ServletException
(sm.getString("standardPipeline.noValve"));
}
}
而在tomcat7中则为:
@Override
public void invoke(Request request, Response response)
throws IOException, ServletException {
pipeline.getFirst().invoke(request, response);
}
在这儿选取AccessLogValve的invoke代码为例:
@Override
public void invoke(Request request, Response response) throws IOException,ServletException {
getNext().invoke(request, response);
}
可以看到最明显的区别tomcat7将Valve调用从循环调用改为了链式调用。