1. 程式人生 > >Tomcat 原始碼分析之Socket通訊

Tomcat 原始碼分析之Socket通訊

Tomcat 原始碼分析之Socket通訊

此係列文章皆為Tomcat 7.0程式碼程式碼分析。

1.    Socket通訊:

Tomcat對於 Socket的處理方式主要分為以下幾種:

  1.  BIO方式:採用Java阻塞Socket通訊的方式處理連線。
  2.  NIO方式:之前採用BIO(阻塞方式),現在由於在Java1.4之後引入NIO,提供了NIO的實現。
  3. APR方式:為了和本地機器更好的整合,有更高的效能,例如一些高階的系統IO功能(sendfile, epoll and OpenSSL),本地操作的處理(shared memory, NT pipes and Unix sockets以及OS Level的功能(random number generation, system status, etc),Tomcat使用JNI呼叫處理Socket連結。
  4. AJP和ARP結合的方式。
  5. AJP方式:通過AJP協議進行通訊 : AJP主要用於Apache的HTTP伺服器和Servlet Web容器之間通訊,它是Packet_Oriented的,換句話說,它傳送給瀏覽器(其他Web Server)的資料是Packet(s),得到Servlet 容器的響應也是Packet(s),這裡有特殊情況,如果Servlet 容器傳送的資料是二進位制的,則直接傳送給瀏覽器。此外,AJP還可以重用和Servlet容器之間的Socket連線(Socket Connection),降低建立開銷。 具體請看:http://httpd.apache.org/docs/2.2/mod/mod_proxy_ajp.html

2.    模型介紹

Connector由ProtocolHandler和一個連線埠組成,ProtocolHandler使用以上介紹的各種方式處理Socket。

根據配置選取不同的ProtocolHandler實現類的程式碼如下:

Java程式碼 
  1. /** 
  2.      * Set the Coyote protocol which will be used by the connector. 
  3.      * 
  4.      * @param protocol The Coyote protocol name 
  5.      */  
  6.     public void setProtocol(String protocol) {  
  7.         if (AprLifecycleListener.isAprAvailable()) {  
  8.             if ("HTTP/1.1".equals(protocol)) {  
  9.                 setProtocolHandlerClassName  
  10.                     ("org.apache.coyote.http11.Http11AprProtocol");  
  11.             } else if ("AJP/1.3".equals(protocol)) {  
  12.                 setProtocolHandlerClassName  
  13.                     ("org.apache.coyote.ajp.AjpAprProtocol");  
  14.             } else if (protocol != null) {  
  15.                 setProtocolHandlerClassName(protocol);  
  16.             } else {  
  17.                 setProtocolHandlerClassName  
  18.                     ("org.apache.coyote.http11.Http11AprProtocol");  
  19.             }  
  20.         } else {  
  21.             if ("HTTP/1.1".equals(protocol)) {  
  22.                 setProtocolHandlerClassName  
  23.                     ("org.apache.coyote.http11.Http11Protocol");  
  24.             } else if ("AJP/1.3".equals(protocol)) {  
  25.                 setProtocolHandlerClassName  
  26.                     ("org.apache.coyote.ajp.AjpProtocol");  
  27.             } else if (protocol != null) {  
  28.                 setProtocolHandlerClassName(protocol);  
  29.             }  
  30.         }  
  31.     }  

其相應的配置例子如下:

Java程式碼 
  1. <Connector port="8080" protocol="HTTP/1.1"  
  2.                connectionTimeout="20000"  
  3.                redirectPort="8443" />  

Connector呼叫ProtocolHandler物件處理Socket,主要程式碼在該Connector類的startInternal()裡,如下

Java程式碼 
  1. /** 
  2.      * Begin processing requests via this Connector. 
  3.      * 
  4.      * @exception LifecycleException if a fatal startup error occurs 
  5.      */  
  6.     @Override  
  7.     protected void startInternal() throws LifecycleException {  
  8.         setState(LifecycleState.STARTING);  
  9.         try {  
  10.             protocolHandler.start();  
  11.         } catch (Exception e) {  
  12.             String errPrefix = "";  
  13.             if(this.service != null) {  
  14.                 errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";  
  15.             }  
  16.             throw new LifecycleException  
  17.                 (errPrefix + " " + sm.getString  
  18.                  ("coyoteConnector.protocolHandlerStartFailed"), e);  
  19.         }  
  20.         mapperListener.start();  
  21. }  

而ProtocolHandler物件會啟動一個相應的AbstractEndpoint物件來建立ServerSocket,監聽服務相應的埠,並啟動執行緒池處理訊息。

ProtocolHandler物件啟動AbstractEndpoint物件的程式碼在org.apache.coyote.AbstractProtocolHandler類裡,如下:

Java程式碼 
  1. @Override  
  2.     public void start() throws Exception {  
  3.         if (getLog().isInfoEnabled())  
  4.             getLog().info(sm.getString("abstractProtocolHandler.start",  
  5.                     getName()));  
  6.         try {  
  7.             endpoint.start();  
  8.         } catch (Exception ex) {  
  9.             getLog().error(sm.getString("abstractProtocolHandler.startError",  
  10.                     getName()), ex);  
  11.             throw ex;  
  12.         }  
  13. }  

各種不同的ProtocolHandler對應的AbstractEndpoint如下:

ProtocolHandler

AbstractEndpoint

AjpAprProtocol

AprEndpoint

AjpProtocol

JIoEndpoint

Http11AprProtocol

AprEndpoint

Http11NioProtocol

NioEndpoint

Http11Protocol

JIoEndpoint

不同協議處理方式請看這個類的實現:AprEndpoint,JIoEndpoint,NioEndpoint。

JIoEndpoint採用BIO方式處理,NioEndpoint採用NIO的方式處理,AprEndpoint呼叫大量的Poll的大量native方法處理Socket。具體不再一一介紹。

我們最後為這三個元件畫出一個簡單的模型,如下:

3.    Tomcat中Server,Service和Connector之間的關係:

一個Server包含多個Service,而一個Service由多個Connector組成。

一個Server對應一個Servlet容器的例項,而一個Service可以由多個Connector組成,但是這些Connector必須是一個Engine的,Engine代表一臺實際的物理或者虛擬機器器,因為Tomcat可以實現叢集的,配置片段示例如下:

Java程式碼
  1. <Service name="Catalina">  
  2.         <Connector port="8080" protocol="HTTP/1.1"  
  3.                connectionTimeout="20000"  
  4.                redirectPort="8443" />  
  5.         <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />  
  6.        <Engine  …>  
  7.                ….  
  8.        </Engine>  
  9. </Service>  

今天就講到這裡了,後續繼續把自己已看的東西整理出來。