StandardEngine分析-tomcat6.x源碼閱讀

2013-09-16java

StandardEngine是什麼

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

  • 設置以及初始化標記
  • 註冊組件,添加MBServer監控
  • 對父容器Service組件狀態的判斷
/**
	 * 初始化
	 */
	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

  • 斷定是否已經啓動和初始化
  • 啓動realm權限管理組件,註冊到MBServer
  • 調用基類的start()方法,啓動容器,在父容器的start()方法中主要完成事件觸發,logger組件,manager組件,realm組件,cluster組件,resource組件,pipeline組件,後臺處理任務的啓動
/**
	 * 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,主要完成如下幾步:

  • 調用父類的stop()方法
  • 中止mbserver
/**
	 * 中止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銷燬,清理佔用資源。主要完成如下幾個步驟:

  • 註銷組件在mbserver,銷燬組件。
/**
	 * 銷燬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成爲可能,並且層次結構更清晰,管理和維護組件更加容易。

該死的拖延症,我恨你

相關文章
相關標籤/搜索