StandardServer分析-tomcat6.x源碼閱讀

2013-09-13html

####StandardServer是什麼 StandardServer標準實現Server接口,從tomcat結構層次圖中知道,Server處於最外層,其餘組件在其內部,起統領作,像一家公司的CEO,負責管理整個公司,Server表明完整的Servlet容器,管理維護Server和全局resource,在各個組件中共享StandardServer資源。在tomcat啓動過程當中由Catalina經過Digester庫加載解析server.xml,建立StandardServer對象,並初始化和啓動Server,Server將本身註冊到JMX上面,經過tomcat管理頁面查看Server狀態。StandardServer除了實現Server接口之外,還使用下列組件來完成功能。java

Lifecycle StandardServer實現Lifecycle接口,Lifecycle是tomcat中關於組件生命週期狀態監控操做監聽的接口,經過Lifecycle提供的9個狀態和5個方法,使得監控組件的狀態更新和在組件不一樣生命週期階段操做成爲可能。
9的狀態:指示組件狀態mysql

  • BEFORE_INIT
  • AFTER_INIT
  • BEFORE_START
  • START
  • AFTER_START
  • BEFORE_STOP
  • STOP
  • AFTER_STOP
  • BEFORE_DESTROY
  • AFTER_DESTROY
  • PERIODIC
  • CONFIG_START
  • CONFIG_STOP

5個方法: 更新組件狀態和添加監聽組件狀態變動通知組件sql

  • addLifecycleListener(LifecycleListener)
  • findLifecycleListeners()
  • removeLifecycleListener(LifecycleListener)
  • start()
  • stop()

addLifecycleListener(LifecycleListener)是註冊LifecycleEvent事件監聽器。LifecycleListener是監聽Lifecycle組件狀態變動觸發的LifecycleEvent事件,當LifecycleListener監聽到LifecycleEvent事件事件時,會調用LifecycleListener方法lifecycleEvent(LifecycleEvent)響應事件。數據庫

MBeanRegistration
StandardServer實現MBeanRegistration接口。MBeanRegistration接口是JMX的MBean方法的內容,實現該接口的目的是將StandardServer組件註冊到JMX中,經過JMX能夠實現對StandardServer的控制。apache

LifecycleSupport
StandardServer的屬性,它的做用就是負責管理Lifecycle接口實現類的LifecycleListener,只有一個到參的構造器,必須在建立對象時傳入Lifecycle,在StandardServer中建立對象時同時將StandardServer自身傳入,
private LifecycleSupport lifecycle = new LifecycleSupport(this);
LifecycleSupport 管理註冊在StandardServer上的監聽器,當監聽到LifecycleEvent,LifecycleSupport 立刻調用方法fireLifecycleEvent(String, Object)遍歷監聽器響應事件,屬性state指明瞭當前Lifecycle的狀態。數組

javax.naming.Context
StandardServer的屬性,是JNDI中的內容,不瞭解,只知道它提供命名服務,也就是根據名字獲取對象以及對象屬性信息等,經過給定資源路徑,而後就能夠獲取資源路徑下面對象,在server.xml中關於資源的配置,從配置信息中能夠看到配置的是tomcat管理頁面登錄帳號權限信息。tomcat

<!-- Global JNDI resources Documentation at /docs/jndi-resources-howto.html -->
	<GlobalNamingResources>
		<!-- Editable user database that can also be used by UserDatabaseRealm 
			to authenticate users -->
		<Resource name="UserDatabase" auth="Container"
			type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved"
			factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
			pathname="conf/tomcat-users.xml" />
	</GlobalNamingResources>

。java和第三方交互時常常使用,例如數據庫驅動,mysql的JDBC和ODBC,java不用關心其實現部分。JNDI理解例子,來自博客lujin55服務器

NamingContextListener
StandardServer的屬性,是JNDI中的內容,不瞭解,比較複雜,只知道是負責監聽javax.naming.Context資源事件。NamingContextListener實現了LifecycleListener、ContainerListener、PropertyChangeListener3個接口,具有監聽Lifecycle組件,Container組件、PropertyChange的事件能力。網絡

NamingResources
StandardServer的屬性,是JNDI中的內容,不瞭解,比較複雜,知道它管理命名資源,將要加載的資源封裝成對象,能夠直接從NamingResources獲取對象了。

PropertyChangeSupport
StandardServer的屬性,參照LifecycleSupport的理解不難看出,PropertyChangeSupport管理對象屬性變化監聽器,跟LifecycleSupport的使命同樣,監聽PropertyChangeEvent事件,而後響應。

StandardServer經過JNDI加載server.xml中配置的資源,完成資源配置,註冊監聽器來監聽事件,同時將本身註冊到ServerFactory 上。StandardServer再次完成資源加載後調用init()方法初始化,實踐調用的是initialize(),在tomcat啓動中,調用初始化方法是由Catalina來完成。在initialize()方法中分4步完成

  • 第一步首先判斷是否已經初始化
  • 第二部觸發INIT_EVENT事件,標記已經初始化(initialized = true)
  • 第三步註冊到MBeanServer服務器,添加監控,能夠在tomcat管理頁面管理Server
  • 第四步遍歷services數組,挨個調用initialize()初始化
/**
	 * Invoke a pre-startup initialization. This is used to allow connectors to
	 * bind to restricted ports under Unix operating environments.
	 */
	public void initialize() throws LifecycleException {
		if (initialized) {
			log.info(sm.getString("standardServer.initialize.initialized"));
			return;
		}
		lifecycle.fireLifecycleEvent(INIT_EVENT, null);
		initialized = true;

		if (oname == null) {
			try {
				oname = new ObjectName("Catalina:type=Server");
				Registry.getRegistry(null, null).registerComponent(this, oname,
						null);
			} catch (Exception e) {
				log.error("Error registering ", e);
			}
		}

		// Register global String cache
		try {
			ObjectName oname2 = new ObjectName(oname.getDomain()
					+ ":type=StringCache");
			Registry.getRegistry(null, null).registerComponent(
					new StringCache(), oname2, null);
		} catch (Exception e) {
			log.error("Error registering ", e);
		}

		// Initialize our defined Services
		for (int i = 0; i < services.length; i++) {
			services[i].initialize();
		}
	}

StandardServer初始化完成後,調用start()方法啓動Server,在start()方法分4步完成

  • 第一步首先判斷Server是否已經啓動
  • 第二部觸發BEFORE_START_EVENT,START_EVENT事件,標記已經啓動(started = true)
  • 第三步遍歷services數組,挨個調用start()初始化
  • 第四步觸發AFTER_START_EVENT事件,通知監聽器
/**
	 * Prepare for the beginning of active use of the public methods of this
	 * component. This method should be called before any of the public methods
	 * of this component are utilized. It should also send a LifecycleEvent of
	 * type START_EVENT to any registered listeners.
	 * 
	 * @exception LifecycleException
	 *                if this component detects a fatal error that prevents this
	 *                component from being used
	 */
	public void start() throws LifecycleException {

		// Validate and update our current component state
		if (started) {
			log.debug(sm.getString("standardServer.start.started"));
			return;
		}

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);

		lifecycle.fireLifecycleEvent(START_EVENT, null);
		started = true;

		// Start our defined Services
		synchronized (services) {//同步
			for (int i = 0; i < services.length; i++) {
				if (services[i] instanceof Lifecycle)
					((Lifecycle) services[i]).start();
				System.out.println(i+":"+services[i].hashCode());//本身加的,爲了驗證catalina是否在Server的services列表中,最後事實證實catalina不在列表中
			}
		}

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);

	}

StandardServer完成啓動後,Catalina在start()方法中調用await()方法,await()中調用StandardServer的await(),StandardServer的await()方法主要完成的功能很是簡單:啓動ServerSocket監聽網絡端口請求關閉Server的請求,從代碼中能夠看出StandardServer使用一個死循環不斷監聽端口,當接收到"SHUTDOWN"命令時跳出循環,回到Catalina的start()方法中。

/**
	 * Wait until a proper shutdown command is received, then return. This keeps
	 * the main thread alive - the thread pool listening for http connections is
	 * daemon threads.
	 */
	public void await() {
		// Negative values - don't wait on port - tomcat is embedded or we just
		// don't like ports
		if (port == -2) {
			// undocumented yet - for embedding apps that are around, alive.
			return;
		}
		if (port == -1) {
			try {
				awaitThread = Thread.currentThread();//當前線程
				while (!stopAwait) {
					try {
						Thread.sleep(10000);
					} catch (InterruptedException ex) {
						// continue and check the flag
					}
				}
			} finally {
				awaitThread = null;
			}
			return;
		}

		// Set up a server socket to wait on
		try {
			awaitSocket = new ServerSocket(port, 1,
					InetAddress.getByName("localhost"));
		} catch (IOException e) {
			log.error("StandardServer.await: create[" + port + "]: ", e);
			return;
		}

		try {
			awaitThread = Thread.currentThread();//當前線程

			// Loop waiting for a connection and a valid command
			while (!stopAwait) {
				ServerSocket serverSocket = awaitSocket;
				if (serverSocket == null) {
					break;
				}

				// Wait for the next connection
				Socket socket = null;
				StringBuilder command = new StringBuilder();
				try {
					InputStream stream = null;
					try {
						socket = serverSocket.accept();
						socket.setSoTimeout(10 * 1000); // Ten seconds
						stream = socket.getInputStream();
					} catch (AccessControlException ace) {
						log.warn("StandardServer.accept security exception: "
								+ ace.getMessage(), ace);
						continue;
					} catch (IOException e) {
						if (stopAwait) {
							// Wait was aborted with socket.close()
							break;
						}
						log.error("StandardServer.await: accept: ", e);
						break;
					}

					// Read a set of characters from the socket
					int expected = 1024; // Cut off to avoid DoS attack
					while (expected < shutdown.length()) {
						if (random == null)
							random = new Random();
						expected += (random.nextInt() % 1024);
					}
					while (expected > 0) {
						int ch = -1;
						try {
							ch = stream.read();
						} catch (IOException e) {
							log.warn("StandardServer.await: read: ", e);
							ch = -1;
						}
						if (ch < 32) // Control character or EOF terminates loop
							break;
						command.append((char) ch);
						expected--;
					}
				} finally {
					// Close the socket now that we are done with it
					try {
						if (socket != null) {
							socket.close();
						}
					} catch (IOException e) {
						// Ignore
					}
				}

				// Match against our command string
				boolean match = command.toString().equals(shutdown);
				if (match) {
					break;
				} else
					log.warn("StandardServer.await: Invalid command '"
							+ command.toString() + "' received");
			}
		} finally {
			ServerSocket serverSocket = awaitSocket;
			awaitThread = null;
			awaitSocket = null;

			// Close the server socket and return
			if (serverSocket != null) {
				try {
					serverSocket.close();
				} catch (IOException e) {
					// Ignore
				}
			}
		}
	}

在StandardServer調出awaite()方法循環回到Catalina的start()方法中,緊跟在awaite()方法後的是stop()方法,也即中止Server的方法,在StandardServer的stop()方法中主要完成中止Server的任務,分5步完成

  • 第一步首先判斷Server是否已經啓動
  • 第二部觸發BEFORE_STOP_EVENT,STOP_EVENT事件,標記已經中止(started = false)
  • 第三步遍歷services數組,挨個調用stop()初始化
  • 第四步觸發AFTER_STOP_EVENT事件,通知監聽器
  • 第五步調用stopAwait(),關閉ServerSocket,調用interrupt()退出線程。
/**
	 * Gracefully terminate the active use of the public methods of this
	 * component. This method should be the last one called on a given instance
	 * of this component. It should also send a LifecycleEvent of type
	 * STOP_EVENT to any registered listeners.
	 * 
	 * @exception LifecycleException
	 *                if this component detects a fatal error that needs to be
	 *                reported
	 */
	public void stop() throws LifecycleException {

		// Validate and update our current component state
		if (!started)
			return;

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);

		lifecycle.fireLifecycleEvent(STOP_EVENT, null);
		started = false;

		// Stop our defined Services
		for (int i = 0; i < services.length; i++) {
			if (services[i] instanceof Lifecycle)
				((Lifecycle) services[i]).stop();
		}

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);

		stopAwait();

	}

至此,StandardServer的初始化,啓動,中止的內容完成了,StandardServer的認識也清晰了不少,StandardServer先從server.xml中加載資源完成配置,提供監聽StandardServer狀態方法,已經纔不一樣生命週期操做StandardServer;在初始化中觸發事件,初始化services,註冊到MBeanServer,標記已經初始化,在啓動中,觸發啓動事件,啓動services,啓動完成後,在由Catalina觸發awaite()方法,啓動ServerSocket監聽請求關閉Server命令,在中止Server stop()方法中,觸發中止Server事件,中止services,清除資源。註冊在StandardServer上的監聽器有點複雜,之後在看看是什麼回事。

通過檢測Catalian不在StandardServer的services列表中,那Catalian爲什麼要實現Service接口呢? 感受JNDI有點意思,有時間好好看看!

啊啊啊...終於寫完了,好累哦,歡迎吐槽...

專注而精,要記得堅持

相關文章
相關標籤/搜索