muugua 发表于 2017-1-30 11:45:25

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>  

   
今天就讲到这里了,后续继续把自己已看的东西整理出来。
页: [1]
查看完整版本: Tomcat 源代码分析之Socket通讯