目錄(?)[-]html
- protocol
- Service
- mapperListener
- 初始化與啓動
以下圖所示,Tomcat服務器主要有兩大核心模塊組成:鏈接器和容器,本節只分析鏈接器的實現。java
![](http://static.javashuo.com/static/loading.gif)
鏈接器主要是接收用戶的請求,而後封裝請求傳遞給容器處理,tomcat中默認的鏈接器是Coyote.首先來看鏈接器的類圖:web
![](http://static.javashuo.com/static/loading.gif)
protocol
咱們發現這個類裏面有不少與protocol有關的屬性和方法,tomcat中支持兩種協議的鏈接器:HTTP/1.1與AJP/1.3,查看tomcat的配置文件server.xml能夠看到以下配置:apache
[java] view plain copy瀏覽器
print?tomcat
- <Connector port="8080" protocol="HTTP/1.1"
- connectionTimeout="20000"
- redirectPort="8443" URIEncoding="utf-8"/>
-
- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
HTTP/1.1協議負責創建HTTP鏈接,web應用經過瀏覽器訪問tomcat服務器用的就是這個鏈接器,默認監聽的是8080端口;服務器
AJP/1.3協議負責和其餘HTTP服務器創建鏈接,監聽的是8009端口,好比tomcat和apache或者iis集成時須要用到這個鏈接器。網絡
協議上有三種不一樣的實現方式:JIO、APR、NIO。app
JIO(java.io):用java.io純JAVA編寫的TCP模塊,這是tomcat默認鏈接器實現方法;異步
APR(Apache Portable Runtime):有C語言和JAVA兩種語言實現,鏈接Apache httpd Web服務器的類庫是在C中實現的,同時用APR進行網絡通訊;
NIO(java.nio):這是用純Java編寫的鏈接器(Conector)的一種可選方法。該實現用java.nio核心Java網絡類以提供非阻塞的TCP包特性。
ProtocolHandler接口是對這些協議的抽象,其類層次結構圖以下呼所示:
![](http://static.javashuo.com/static/loading.gif)
前面提到tomcat默認採用的是Http11Protocol,要麼要怎麼更換默認的protocolHandler呢,先看看setProtocol方法的源碼:
[java] view plain copy
print?
- public void setProtocol(String protocol) {
-
- if (AprLifecycleListener.isAprAvailable()) {
- if ("HTTP/1.1".equals(protocol)) {
- setProtocolHandlerClassName
- ("org.apache.coyote.http11.Http11AprProtocol");
- } else if ("AJP/1.3".equals(protocol)) {
- setProtocolHandlerClassName
- ("org.apache.coyote.ajp.AjpAprProtocol");
- } else if (protocol != null) {
- setProtocolHandlerClassName(protocol);
- } else {
- setProtocolHandlerClassName
- ("org.apache.coyote.http11.Http11AprProtocol");
- }
- } else {
- if ("HTTP/1.1".equals(protocol)) {
- setProtocolHandlerClassName
- ("org.apache.coyote.http11.Http11Protocol");
- } else if ("AJP/1.3".equals(protocol)) {
- setProtocolHandlerClassName
- ("org.apache.coyote.ajp.AjpProtocol");
- } else if (protocol != null) {
- setProtocolHandlerClassName(protocol);
- }
- }
- }
從以上代碼能夠看出只要protocol不爲HTTP/1.1也不爲AJP/1.3就會調用setProtocolHandlerClassName來設置protocolHandler,也就是說要替換默認的protocolHandler,只須要修改server.xml文件Connector中的protocol屬性便可!
Service
鏈接器和容器一塊兒才能對外提供服務,Service裏面包含了一個容器和多個鏈接器,鏈接器是怎麼加入到Service中的,能夠看一下StandardService中的代碼:
[html] view plain copy
print?
- public void addConnector(Connector connector) {
-
- synchronized (connectors) {
- connector.setService(this);
- Connector results[] = new Connector[connectors.length + 1];
- System.arraycopy(connectors, 0, results, 0, connectors.length);
- results[connectors.length] = connector;
- connectors = results;
-
- if (getState().isAvailable()) {
- try {
- connector.start();
- } catch (LifecycleException e) {
- log.error(sm.getString(
- "standardService.connector.startFailed",
- connector), e);
- }
- }
-
- // Report this property change to interested listeners
- support.firePropertyChange("connector", null, connector);
- }
-
- }
StandardService中還有setContainer方法,正是Service把容器和鏈接器關聯在一塊兒的
mapperListener
在鏈接器初始化的時候會初始化mapperListener,mapperListener在初始化的時候會調用mapper對象的addXXX方法
![](http://static.javashuo.com/static/loading.gif)
Mapper對象在tomcat中存在於兩個地方:
一、每一個context容器對象中,它只記錄了此context內部的訪問資源與相對應的wrapper子容器的映射;
二、connector模塊中,這是tomcat全局的變量,它記錄了一個完整的映射對應關係,即根據訪問的完整URL如何定位到哪一個host下的哪一個context的哪一個wrapper容器。
Servlet中forward跳轉會用到第一種mapper,也就是說forward是服務器內部的重定向。
初始化與啓動
Connector的初始化過程以下:
一、Tomcat初始化時會調用Bootstrap的Load方法,這裏會解析XML文件,Digester解析的過程當中,會調用Connector的構造方法
[html] view plain copy
print?
- public Connector(String protocol) {
- setProtocol(protocol);
- // Instantiate protocol handler
- try {
- Class<?> clazz = Class.forName(protocolHandlerClassName);
- this.protocolHandler = (ProtocolHandler) clazz.newInstance();
- } catch (Exception e) {
- log.error(sm.getString(
- "coyoteConnector.protocolHandlerInstantiationFailed"), e);
- }
- }
這個構造方法首先設置protocol,而後初始化protocolhandler
二、緊接着初始化Server,一個Server可能有多個Service,在初始化Server的過程當中會初始化多個Service.Service由一個容器和多個鏈接器組成,會先初始化容器,而後初始化化兩個鏈接器,鏈接器的初始化是調用的Connector的initInternal方法
[html] view plain copy
print?
- protected void initInternal() throws LifecycleException {
-
- super.initInternal();
-
- // Initialize adapter
- adapter = new CoyoteAdapter(this);
- protocolHandler.setAdapter(adapter);
-
- // Make sure parseBodyMethodsSet has a default
- if( null == parseBodyMethodsSet ) {
- setParseBodyMethods(getParseBodyMethods());
- }
-
- try {
- protocolHandler.init();
- } catch (Exception e) {
- throw new LifecycleException
- (sm.getString
- ("coyoteConnector.protocolHandlerInitializationFailed"), e);
- }
-
- // Initialize mapper listener
- mapperListener.init();
- }
(1)首先爲protocolHandler設置一個adapter,而後初始化protocolHandler
(2)初始化mapperListener,啥都沒作,只是調用了父類(LifecycleMBeanBase)的initInternal方法!
因爲Tomcat的生命週期控制,鏈接器的啓動過程和初始化過程幾乎同樣,也是由Catalina的start方法開始,Server啓動,Service啓動,Container啓動,Connector啓動。Connector啓動調用了它的startInternal方法,這個方法只作了兩件事:啓動protocolHandler和mapperListener,鏈接器的啓動過程就是這樣!下面分別來看看protocolHandler和mapperListener啓動時都作了哪些事?
protocolHandler的初始化是在其父類AbstractProtocol的start方法中完成的,
[html] view plain copy
print?
- public void start() throws Exception {
- if (getLog().isInfoEnabled())
- getLog().info(sm.getString("abstractProtocolHandler.start",
- getName()));
- try {
- endpoint.start();
- } catch (Exception ex) {
- getLog().error(sm.getString("abstractProtocolHandler.startError",
- getName()), ex);
- throw ex;
- }
- }
調用了endpoint的start方法,這個方法裏面作了如下幾件事:
(1)設置接收線程數和最大鏈接數,建立socket
(2)建立線程池,啓動監聽的線程監聽用戶請求
(3)啓動一個線程來負責異步請求
mapperListener也繼承了LifecycleMBeanBase類,也是受生命週期控制的。因此它的啓動是在startInternal方法中完成的
[html] view plain copy
print?
- public void startInternal() throws LifecycleException {
-
- setState(LifecycleState.STARTING);
-
- // Find any components that have already been initialized since the
- // MBean listener won't be notified as those components will have
- // already registered their MBeans
- findDefaultHost();
-
- Engine engine = (Engine) connector.getService().getContainer();
- addListeners(engine);
-
- Container[] conHosts = engine.findChildren();
- for (Container conHost : conHosts) {
- Host host = (Host) conHost;
- if (!LifecycleState.NEW.equals(host.getState())) {
- // Registering the host will register the context and wrappers
- registerHost(host);
- }
- }
- }
(1)註冊已初始化的組件
(2)爲各類容器添加監聽器
(3)爲各類容器創建映射關係