2013-09-16java
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的功能。apache
/** * 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。tomcat
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或者傳遞控制。app
ValveBase
是Valve接口的實現,同時實現了Contained,MBeanRegistration接口,具有粘附容器和添加JMX監控的能力,方法backgroundProcess(),invoke(Request, Response)提供空實現,不作任何邏輯操做,將邏輯定義放置到子類中去。本類所完成的功能是Valve的next,保存Valve的下一個Valve,以及Contained接口的方法功能,依附容器。負載均衡
addChild(Container)
添加子容器,在方法內部是調用父類的方法添加子容器。容器嵌套是容器的基本功能之一。dom
init()
負責StandardEngine容器的初始化。主要完成如下幾步操做:jvm
/** * 初始化 */ 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()方法中主要完成如下幾步操做:ide
/** * 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成爲可能,並且層次結構更清晰,管理和維護組件更加容易。
該死的拖延症,我恨你