前兩篇我們分別粗線條和細粒度的講解了tomcat的服務是如何啟動以及連接器Connector和容器Container又分別是如何被啟動的.
本篇我們主要側重tomcat中server、service以及connector和container之間是如何相互關聯起來的。在此之前,我們分別看下這個類中的一些主要方法,尤其是用於相互關聯綁定的方法。
Server:(Server代表了整個Catalina容器,一個server可以包含一個或多個Services)
1 getInfo //獲取server的版本 2 getGlobalNamingResources 3 setGlobalNamingResources 4 getPort //返回監聽關閉server的端口 5 setPort 6 getShutdown //返回關閉server的命令字符串比如"SHUTDOWN" 7 setShutdown 8 addService //在該server上添加一個service 9 await //一直監聽,直到出現shutdown指令 10 findService //返回指定名稱的service 11 findServices //返回所有在這個server上的services集合 12 removeService 13 initialize
Service:(Service是一組包含了一個Container和一個或多個Connector的集合)
1 getContainer //返回容器,該容器用於處理Service上的Connectors發送過來請求 2 setContainer 3 getInfo //返回Service的版本信息 4 getName //返回該Service的名字 5 setName 6 getServer //返回與此Service關聯的Server,這與Server中的addService遙相呼應 7 setServer //綁定一個Server 8 addConnector //添加一個Connector 9 findConnectors //返回該Service上的所有Connector 10 removeConnector //刪除指定的Connector,同時也以為該Connector與Container也解除聯系 11 initialize 12 addExecutor //添加一個執行器 13 findExecutors 14 getExecutor 15 removeExecutor
Connector:(前面已經說過,一個Service中可以包含多個Container,但是只會有一個Connector,而Container有多層實現關系,並且有自己的實現規范,所以定義成了接口,而這里的Connector就是一個類而非接口)
1 Connector 2 Connector //構造函數,其中有設置Connector要用到的協議 3 getProperty //根據屬性名,返回屬性值 4 setProperty 5 getAttribute //也是根據屬性名,返回屬性值,但是getProperty返回的是String類型,這里是Object對象 6 setAttribute 7 removeProperty 8 getService //返回與之綁定的Service 9 setService //綁定Service 10 getAllowTrace 11 setAllowTrace //設置allowTrace,用於跟蹤http的信息 12 isAvailable //判斷是否可用於處理request,里面判斷的標記是started,這意味着只有Connector啟動了才能用於處理request 13 getBufferSize 14 setBufferSize 15 getContainer //返回當前Connector移交request的接收Container對象 16 setContainer 17 getEmptySessionPath 18 setEmptySessionPath 19 getEnableLookups 20 setEnableLookups 21 getInfo //返回Connector的版本信息 22 getMapper 23 getMaxHeaderCount //返回Container允許的最大headers個數 24 setMaxHeaderCount 25 getMaxParameterCount //返回GET和POST方法的最大個數 26 setMaxParameterCount 27 ... 28 getPort //返回監聽request的端口 29 setPort 30 getProtocol //返回使用到的protocol handler,有Http/1.1和AJP/1.3 31 setProtocol 32 getProtocolHandlerClassName 33 setProtocolHandlerClassName 34 getProtocolHandler 35 getProxyName //設置代理的名字 36 setProxyName 37 getProxyPort 38 setProxyPort 39 getRedirectPort //重定向端口,如果 40 setRedirectPort 41 getScheme 42 setScheme 43 getSecure 44 setSecure 45 getURIEncoding //返回URI編碼 46 setURIEncoding 47 getUseBodyEncodingForURI 48 setUseBodyEncodingForURI 49 getXpoweredBy 50 setXpoweredBy 51 setUseIPVHosts 52 getUseIPVHosts 53 getExecutorName 54 createRequest //創建或指派並返回Request對象,這里的Request有和Container關聯 55 createResponse 56 addLifecycleListener 57 findLifecycleListeners 58 removeLifecycleListener 59 createObjectName 60 initialize //初始化Connector對象 61 pause 62 resume 63 start 64 stop 65 ... 66 init 67 destroy 68 toString
Container:(Container可以執行來自客戶端的Request請求,並返回相應的Response)
getInfo getLoader //返回與此Container相關的Loader對象,如果沒有Loader,則返回與其父Container關聯的Loader setLoader getLogger //返回Logger對象,用於打印log,同理如果當前沒有Logger對象,則尋找父級Logger getManager //返回Manager對象 setManager getMappingObject //返回JMX對象名字 getObjectName getPipeline //返回與此Container相關的用於管理Valves的Pipeline getCluster setCluster getBackgroundProcessorDelay setBackgroundProcessorDelay getName setName getParent //返回父級Container setParent getParentClassLoader //返回父級類加載器 setParentClassLoader getRealm setRealm getResources setResources backgroundProcess addChild //添加一個子容器,在添加之前,需要在子容器中先調用setParent方法 addContainerListener //添加事件監聽器 addPropertyChangeListener //添加屬性值變化監聽器 findChild findChildren findContainerListeners invoke //執行具體的Request,並得到具體的Response對象 removeChild removeContainerListener removePropertyChangeListener logAccess
1.連接原理舉例
首先我們在Catalina類的load方法中調用了方法createStartDigester,該方法在之前幾篇有介紹過,主要是對於加載的server.xml文件中定義各個組件之間的關系。
比如方法中的片段:
digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className"); digester.addSetProperties("Server"); digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");
- addObjectCreate就是添加一個模式,當解析server.xml遇到Server的時候,就根據Server的className實例化一個Server對象,而默認實例化的類就是org.apache.catalina.core.StandardServer;
- addSetProperties用於設置Server的一些屬性,具體屬性在server.xml中有定義;
- addSetNext用於調用Server類的setServer方法,把當前Server添加進去。
再比如這幾行代碼:
digester.addObjectCreate("Server/Listener", null, // MUST be specified in the element "className"); digester.addSetProperties("Server/Listener"); digester.addSetNext("Server/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");
對應在server.xml中就是這幾行
1 <!--APR library loader. Documentation at /docs/apr.html --> 2 <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> 3 <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html --> 4 <Listener className="org.apache.catalina.core.JasperListener" /> 5 <!-- Prevent memory leaks due to use of particular java/javax APIs--> 6 <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> 7 <!-- JMX Support for the Tomcat server. Documentation at /docs/non-existent.html --> 8 <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" /> 9 <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
- 同理addObjectCreate說的是在Server下的Listeners,根據className創建listener對象;
- addSetProperties用於設置Server的一些屬性,具體屬性在server.xml中有定義;
- addSetNext用於調用Server類的addLifecycleLisntener方法,把server.xml中定義的5個監聽器都實例化並添加到server上。
還有關於Server和Service之間關系的代碼
digester.addObjectCreate("Server/Service", "org.apache.catalina.core.StandardService", "className"); digester.addSetProperties("Server/Service"); digester.addSetNext("Server/Service", "addService", "org.apache.catalina.Service");
通過這些代碼我們很容易理解Server和Service之間的關聯關系(后面會詳細介紹)
除了Server和Service之間的從屬關系,我們還可以看到Service和Connector之間的關系
digester.addRule("Server/Service/Connector", new ConnectorCreateRule()); digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(new String[]{"executor"})); digester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.connector.Connector");
同理這里也是在Service下調用addConnector添加Connector(后面會詳細介紹)
2.Connector和Container以及Connector和Service何時連接?
我們從Catalina的load方法開始,當執行到load中的digester.parse(inputSource)時,即跳轉到Digester類的parse方法中,之后開始解析server.xml中依次遇到的各個元素。
當遇到server元素的時候,在代碼中方法的執行順序為Catalina.load->Digester.parse->Digester.startElement
startElement方法如下:

1 public void startElement(String namespaceURI, String localName, 2 String qName, Attributes list) 3 throws SAXException { 4 boolean debug = log.isDebugEnabled(); 5 6 if (saxLog.isDebugEnabled()) { 7 saxLog.debug("startElement(" + namespaceURI + "," + localName + "," + 8 qName + ")"); 9 } 10 11 // Parse system properties 12 list = updateAttributes(list); 13 14 // Save the body text accumulated for our surrounding element 15 bodyTexts.push(bodyText); 16 if (debug) { 17 log.debug(" Pushing body text '" + bodyText.toString() + "'"); 18 } 19 bodyText = new StringBuffer(); 20 21 // the actual element name is either in localName or qName, depending 22 // on whether the parser is namespace aware 23 String name = localName; 24 if ((name == null) || (name.length() < 1)) { 25 name = qName; 26 } 27 28 // Compute the current matching rule 29 StringBuffer sb = new StringBuffer(match); 30 if (match.length() > 0) { 31 sb.append('/'); 32 } 33 sb.append(name); 34 match = sb.toString(); 35 if (debug) { 36 log.debug(" New match='" + match + "'"); 37 } 38 39 // Fire "begin" events for all relevant rules 40 List rules = getRules().match(namespaceURI, match); 41 matches.push(rules); 42 if ((rules != null) && (rules.size() > 0)) { 43 for (int i = 0; i < rules.size(); i++) { 44 try { 45 Rule rule = (Rule) rules.get(i); 46 if (debug) { 47 log.debug(" Fire begin() for " + rule); 48 } 49 rule.begin(namespaceURI, name, list); 50 } catch (Exception e) { 51 log.error("Begin event threw exception", e); 52 throw createSAXException(e); 53 } catch (Error e) { 54 log.error("Begin event threw error", e); 55 throw e; 56 } 57 } 58 } else { 59 if (debug) { 60 log.debug(" No rules found matching '" + match + "'."); 61 } 62 } 63 64 }
當執行到rule.begin(namespaceURI, name, list)這行的時候,通過調試信息可以看到該rule的className為org.apache.catalina.core.StandardServer,所以最終會進入StandardServer的構造函數中。
另外,當解析server.xml到5個listener的時候,就會調用StandardServer的addLifecycleListener分別將這5個監聽器實例化並添加到server上。
繼續解析,當解析到
igester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.connector.Connector");
的時候就會跳轉到StandardService的addConnector方法中
1 public void addConnector(Connector connector) { 2 3 synchronized (connectors) { 4 connector.setContainer(this.container); 5 connector.setService(this); 6 Connector results[] = new Connector[connectors.length + 1]; 7 System.arraycopy(connectors, 0, results, 0, connectors.length); 8 results[connectors.length] = connector; 9 connectors = results; 10 11 if (initialized) { 12 try { 13 connector.initialize(); 14 } catch (LifecycleException e) { 15 log.error(sm.getString( 16 "standardService.connector.initFailed", 17 connector), e); 18 } 19 } 20 21 if (started && (connector instanceof Lifecycle)) { 22 try { 23 ((Lifecycle) connector).start(); 24 } catch (LifecycleException e) { 25 log.error(sm.getString( 26 "standardService.connector.startFailed", 27 connector), e); 28 } 29 } 30 31 // Report this property change to interested listeners 32 support.firePropertyChange("connector", null, connector); 33 } 34 35 }
首先解析到的是server.xml中的這個connector
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
下面的兩行代碼交代了一個Connector是如何關聯上Container和Service的:
- connector.setContainer(this.container):說明了connector和container是如何關聯的,調用connector對象的setContainer方法,而傳進的值為this.Container,也就是當前StandardService的Container對象,這樣就完成了Connector和Container之間的連接
- connector.setService(this):對外這里綁定了當前的StandardService作為其從屬的service。
3.Service和Container是何時連接的?
繼續解析直到Catalina.createStartDigester定義的
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
這時候會調用StandardService的setContainer方法:
1 public void setContainer(Container container) { 2 3 Container oldContainer = this.container; 4 if ((oldContainer != null) && (oldContainer instanceof Engine)) 5 ((Engine) oldContainer).setService(null); 6 this.container = container; 7 if ((this.container != null) && (this.container instanceof Engine)) 8 ((Engine) this.container).setService(this); 9 if (started && (this.container != null) && 10 (this.container instanceof Lifecycle)) { 11 try { 12 ((Lifecycle) this.container).start(); 13 } catch (LifecycleException e) { 14 ; 15 } 16 } 17 synchronized (connectors) { 18 for (int i = 0; i < connectors.length; i++) 19 connectors[i].setContainer(this.container); 20 } 21 if (started && (oldContainer != null) && 22 (oldContainer instanceof Lifecycle)) { 23 try { 24 ((Lifecycle) oldContainer).stop(); 25 } catch (LifecycleException e) { 26 ; 27 } 28 } 29 30 // Report this property change to interested listeners 31 support.firePropertyChange("container", oldContainer, this.container); 32 33 }
當執行到((Engine) this.container).setService(this);這里會跳轉到StandardEngine的setService交代了container是如何綁定StandardService的。
並且在代碼
1 synchronized (connectors) { 2 for (int i = 0; i < connectors.length; i++) 3 connectors[i].setContainer(this.container); 4 }
我們可以看到通過遍歷所有的connector,將其與container綁定。
4.Server和Service又是何時連接的?
繼續解析直到Catalina.createStartDigerster中的
digester.addSetNext("Server/Service", "addService", "org.apache.catalina.Service");
會調用StandardServer的addService方法
1 public void addService(Service service) { 2 3 service.setServer(this); 4 5 synchronized (services) { 6 Service results[] = new Service[services.length + 1]; 7 System.arraycopy(services, 0, results, 0, services.length); 8 results[services.length] = service; 9 services = results; 10 11 if (initialized) { 12 try { 13 service.initialize(); 14 } catch (LifecycleException e) { 15 log.error(e); 16 } 17 } 18 19 if (started && (service instanceof Lifecycle)) { 20 try { 21 ((Lifecycle) service).start(); 22 } catch (LifecycleException e) { 23 ; 24 } 25 } 26 27 // Report this property change to interested listeners 28 support.firePropertyChange("service", null, service); 29 } 30 31 }
service.setServer(this):該行會調用StandardService中的setServer為service綁定當前的StandardServer對象
5.小結
當server.xml中的rule解析完畢后,我們起碼明白了:
- Server和Service是如何關聯的;
- Service和Connector是如何關聯的;
- Service和Container是如何關聯的;
- Connector和Container是如何關聯的
如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!如果您想持續關注我的文章,請掃描二維碼,關注JackieZheng的微信公眾號,我會將我的文章推送給您,並和您一起分享我日常閱讀過的優質文章。
友情贊助
如果你覺得博主的文章對你那么一點小幫助,恰巧你又有想打賞博主的小沖動,那么事不宜遲,趕緊掃一掃,小額地贊助下,攢個奶粉錢,也是讓博主有動力繼續努力,寫出更好的文章^^。
1. 支付寶 2. 微信