Tomcat 原始碼分析之Socket通訊
Tomcat 原始碼分析之Socket通訊
此係列文章皆為Tomcat 7.0程式碼程式碼分析。
1. Socket通訊:
Tomcat對於 Socket的處理方式主要分為以下幾種:
- BIO方式:採用Java阻塞Socket通訊的方式處理連線。
- NIO方式:之前採用BIO(阻塞方式),現在由於在Java1.4之後引入NIO,提供了NIO的實現。
- APR方式:為了和本地機器更好的整合,有更高的效能,例如一些高階的系統IO功能(sendfile, epoll and OpenSSL),本地操作的處理(shared memory, NT pipes and Unix sockets以及OS Level的功能(random number generation, system status, etc),Tomcat使用JNI呼叫處理Socket連結。
- AJP和ARP結合的方式。
- 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程式碼- /**
- * Set the Coyote protocol which will be used by the connector.
- *
- * @param protocol The Coyote protocol name
- */
- 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);
- }
- }
- }
其相應的配置例子如下:
Java程式碼- <Connector port="8080" protocol="HTTP/1.1"
- connectionTimeout="20000"
- redirectPort="8443" />
Connector呼叫ProtocolHandler物件處理Socket,主要程式碼在該Connector類的startInternal()裡,如下
Java程式碼- /**
- * Begin processing requests via this Connector.
- *
- * @exception LifecycleException if a fatal startup error occurs
- */
- @Override
- protected void startInternal() throws LifecycleException {
- setState(LifecycleState.STARTING);
- try {
- protocolHandler.start();
- } catch (Exception e) {
- String errPrefix = "";
- if(this.service != null) {
- errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
- }
- throw new LifecycleException
- (errPrefix + " " + sm.getString
- ("coyoteConnector.protocolHandlerStartFailed"), e);
- }
- mapperListener.start();
- }
而ProtocolHandler物件會啟動一個相應的AbstractEndpoint物件來建立ServerSocket,監聽服務相應的埠,並啟動執行緒池處理訊息。
ProtocolHandler物件啟動AbstractEndpoint物件的程式碼在org.apache.coyote.AbstractProtocolHandler類裡,如下:
Java程式碼- @Override
- 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;
- }
- }
各種不同的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程式碼- <Service name="Catalina">
- <Connector port="8080" protocol="HTTP/1.1"
- connectionTimeout="20000"
- redirectPort="8443" />
- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
- <Engine …>
- ….
- </Engine>
- </Service>
今天就講到這裡了,後續繼續把自己已看的東西整理出來。