2013-09-16
standardEngine是org.apache.catalina.Engine接口的标准实现,继承自ContainerBase,具备容器的功能。Engine的上级领导是Service,角色是负责管理虚拟主机,一个Engine就是一个Servlet容器,能够完成处理请求的任务,Engine从Connector连接器接收请求,然后分发到响应的虚拟主机处理。当需要tomcat需要使用多个虚拟机(配置jvmRoute)来实现负载均衡时,就需要用到Engine。在tomcat中,Engine不是必须的,但是tomcat都会启用Engine组件。Engine的功能:从Connector连接中接收请求,为请求选择虚拟机,分发到虚拟主机中处理请求。
StandardEngine()
StandardEngine在默认构造器中准备所需资源的常量配置。调用super()方式初始化父类,也即ContainerBase;然后为pipeline阀门链(类似FilterChain)设置基准Valve,再者设置jvmRoute虚拟机路由,最后设置Engine后台处理线程的时间间隔。StandardEngine需要使用到这些内容来完成Engine的功能。
/**
* Create a new StandardEngine component with the default basic Valve.
*/
public StandardEngine() {
super();
pipeline.setBasic(new StandardEngineValve());
/* Set the jmvRoute using the system property jvmRoute */
try {
//虚拟机路由,集群使用
setJvmRoute(System.getProperty("jvmRoute"));
} catch (Exception ex) {
}
// By default, the engine will hold the reloading thread
backgroundProcessorDelay = 10;
}
ContainerBase
是tomcat的基容器,实现Container容器接口,具有容器的特点和功能。StandardEngine继承了ContainerBase,Engine是容器,具备容器所具备的功能,Engine的父容器是Service,子容器是Host。
StandardEngineValve
Valve在tomcat中蛮重要的,因为所有的请求和响应都必需通过它来过滤,负责传递请求和响应信息,多个Valve组成Pipeline Valve链,每个容器都具有一个Pipeline链,同时有一个BasicValve,BasicValve的作用非常重要,BasicValve在Pipeline链的尾部,负责调用传递请求信息,将控制转到子容器中的Pipeline中。每个容器都有自己的BasicValve,StandardEngineValve就是Engine的BasicValve,继承自ValveBase。StandardEngineValve的方法invoke(Request, Response)负责将请求传递到Host组件中。
/**
* Select the appropriate child Host to process this request, based on the
* requested server name. If no matching Host can be found, return an
* appropriate HTTP error.
*
* @param request
* Request to be processed
* @param response
* Response to be produced
* @param valveContext
* Valve context used to forward to the next Valve
*
* @exception IOException
* if an input/output error occurred
* @exception ServletException
* if a servlet error occurred
*/
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Host to be used for this Request
Host host = request.getHost();//选择host
if (host == null) {
response.sendError(
HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getServerName()));
return;
}
// Ask this Host to process this request
host.getPipeline().getFirst().invoke(request, response);
}
Valve
过滤Request/Response的接口,类似于Filter,两者功能类似,但是作用阶段不一样。Valve由tomcat控制,多个Valve组成一个Pipeline Valve链,每一个容器组件由于一个Pipeline,Filter是程序员定义逻辑,作用范围比Valve小。方法setNext(Valve)是设置本Valve的下一个Valve,Valve自己本身就是一个单向链表的节点,方法backgroundProcess()是后台任务处理,与容器的backgroundProcess()功能相同,方法invoke(Request, Response)是Valve的核心,也即它的功能点所在,负责过滤Request/Response或者传递控制。
ValveBase
是Valve接口的实现,同时实现了Contained,MBeanRegistration接口,具备粘附容器和添加JMX监控的能力,方法backgroundProcess(),invoke(Request, Response)提供空实现,不做任何逻辑操作,将逻辑定义放置到子类中去。本类所完成的功能是Valve的next,保存Valve的下一个Valve,以及Contained接口的方法功能,依附容器。
addChild(Container)
添加子容器,在方法内部是调用父类的方法添加子容器。容器嵌套是容器的基本功能之一。
init()
负责StandardEngine容器的初始化。主要完成以下几步操作:
/**
* 初始化
*/
public void init() {
if (initialized)
return;
initialized = true;//标记初始化
if (oname == null) {
// not registered in JMX yet - standalone mode
try {
if (domain == null) {
domain = getName();
}
if (log.isDebugEnabled())
log.debug("Register " + domain);
oname = new ObjectName(domain + ":type=Engine");
controller = oname;
//登记组件
Registry.getRegistry(null, null).registerComponent(this, oname,
null);
} catch (Throwable t) {
log.info("Error registering ", t);
}
}
//MBean监控
if (mbeansFile == null) {
String defaultMBeansFile = getBaseDir()
+ "/conf/tomcat5-mbeans.xml";
File f = new File(defaultMBeansFile);
if (f.exists())
mbeansFile = f.getAbsolutePath();
}
if (mbeansFile != null) {
readEngineMbeans();
}
if (mbeans != null) {
try {
Registry.getRegistry(null, null).invoke(mbeans, "init", false);
} catch (Exception e) {
log.error("Error in init() for " + mbeansFile, e);
}
}
// not needed since the following if statement does the same thing the
// right way
// remove later after checking
// if( service==null ) {
// try {
// ObjectName serviceName=getParentName();
// if( mserver.isRegistered( serviceName )) {
// log.info("Registering with the service ");
// try {
// mserver.invoke( serviceName, "setContainer",
// new Object[] { this },
// new String[] { "org.apache.catalina.Container" } );
// } catch( Exception ex ) {
// ex.printStackTrace();
// }
// }
// } catch( Exception ex ) {
// log.error("Error registering with service ");
// }
// }
if (service == null) {//如果服务没有创建
// for consistency...: we are probably in embeded mode
try {
//创建标准服务
service = new StandardService();
//设在容器所属
service.setContainer(this);
//初始化
service.initialize();
// Use same name for Service
service.setName(getName());
} catch (Throwable t) {
log.error(t);
}
}
}
start()
负责StandardEngine容器的启动任务,在start()方法中主要完成以下几步操作:
/**
* Start this Engine component.
* 启动Engine
*
* @exception LifecycleException
* if a startup error occurs
*/
public void start() throws LifecycleException {
if (started) {
return;
}
if (!initialized) {
init();//初始化
}
// Look for a realm - that may have been configured earlier.
// If the realm is added after context - it'll set itself.
if (realm == null) {
ObjectName realmName = null;
try {
realmName = new ObjectName(domain + ":type=Realm");
if (mserver.isRegistered(realmName)) {
mserver.invoke(realmName, "init", new Object[] {},
new String[] {});
}
} catch (Throwable t) {
log.debug("No realm for this engine " + realmName);
}
}
// Log our server identification information
// System.out.println(ServerInfo.getServerInfo());
if (log.isInfoEnabled())
log.info("Starting Servlet Engine: " + ServerInfo.getServerInfo());
if (mbeans != null) {
try {
Registry.getRegistry(null, null).invoke(mbeans, "start", false);
} catch (Exception e) {
log.error("Error in start() for " + mbeansFile, e);
}
}
// Standard container startup
super.start();//启动
}
stop()
负责停止Engine,主要完成以下几步:
/**
* 停止engine
*/
public void stop() throws LifecycleException {
super.stop();//停止
if (mbeans != null) {
try {
Registry.getRegistry(null, null).invoke(mbeans, "stop", false);
} catch (Exception e) {
log.error("Error in stop() for " + mbeansFile, e);
}
}
}
destroy()
负责Engine销毁,清理占用资源。主要完成以下几个步骤:
/**
* 销毁Engine
*/
public void destroy() throws LifecycleException {
if (!initialized)
return;
initialized = false;//标记未初始化
// if we created it, make sure it's also destroyed
// this call implizit this.stop()
((StandardService) service).destroy();//销毁服务
if (mbeans != null) {
try {
Registry.getRegistry(null, null).invoke(mbeans, "destroy",
false);
} catch (Exception e) {
log.error(sm.getString(
"standardEngine.unregister.mbeans.failed", mbeansFile),
e);
}
}
//
if (mbeans != null) {
try {
for (int i = 0; i < mbeans.size(); i++) {
Registry.getRegistry(null, null).unregisterComponent(
(ObjectName) mbeans.get(i));
}
} catch (Exception e) {
log.error(sm.getString(
"standardEngine.unregister.mbeans.failed", mbeansFile),
e);
}
}
// force all metadata to be reloaded.
// That doesn't affect existing beans. We should make it per
// registry - and stop using the static.
Registry.getRegistry(null, null).resetMetadata();
}
logAccess(Request, Response, long, boolean)
日志记录方法,负责记录请求信息和响应信息,对于监控和调试很有帮助。
AccessLogListener
负责监听请求,如果有请求则触发事件通知日志记录。
StandardEngine写的马马虎虎,认识程度还不够深。Engine作为连接Connector和Host的角色,Connector负责监听网络端口,接收请求信息,Engine负责将请求信息转到指定的Host中处理。Engine管理一个或者多个Host,可以在一个Engine中配置多个Host,不同的Host应该由系统中配置的jvmRouteId来运行。Engine作为tomcat中标准的容器,衔接Service和Host,但是也可以没有Engine组件,可以直接将请求转到Servlet中处理,Engine的存在使得多个Host成为可能,而且层次结构更清晰,管理和维护组件更加容易。
该死的拖延症,我恨你