tomcat源码分析-Connector初始化与启动

简介:

   一个应用应用服务器的性能很大程度上取决于网络通信模块的实现,因而Connector模块对于tomcat来说是重中之重。从tomcat5开始,默认的连接器实现为Coyote实现(orag.apache.tomcat:tomcat-coyote:7.0.57),本文基于coyote实现会回答如下两个问题:

  •     一个http请求是怎么被tomcat监听到的,会有那些处理;
  •     ajp协议干什么用的。

一、Connector配置

    通过对Container的初始化分析,我们很自然的会回过头来看conf/server.xml的配置,其中配置了2个Connector。 

Xml代码   收藏代码
  1. <Connector port="8080" protocol="HTTP/1.1"  
  2.                connectionTimeout="20000"  
  3.                redirectPort="8443" />  
  4. <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />  

为什么会有多个Connector呢?我们部署服务器的时候,通常会有2种方式:

    1 直接部署tomcat,在浏览器中请求http与tomcat直连
    2 部署一个nginx作反向代理,tomcat与nginx直连

这就是上面两种配置,通过协议protocol来区分。所以多个connector的好处是通过不同的协议,是tomcat服务器能够随着http的应用场景,服务器架构的升级而兼容起来。

    好的,现在配置了2个Connector,那么继续思考一下,Connector是通信过程,如果是你你会怎么设计?显然需要做3件事:

(1)监听端口,创建服务端与客户端的链接;
(2)获取到客户端请求的socket数据,并对Socket数据进行解析和包装成Http请求数据格式;
(3)将包装后的数据交给Container处理

通过源码来分析,Connector有两个属性:protocolHandler(协议)和adapter(适配器),其中protocolHandler完成的是步骤(1)(2),adapter完成的是步骤(3)。

 

二、Connector初始化

    1. Connector构造函数

    在Connector的构造方法中,通过反射生成protocolHandler。

Java代码   收藏代码
  1. public Connector(String protocol) {  
  2.     setProtocol(protocol);  
  3.     // Instantiate protocol handler  
  4.     try {  
  5.         Class<?> clazz = Class.forName(protocolHandlerClassName);  
  6.         this.protocolHandler = (ProtocolHandler) clazz.newInstance();  
  7.     } catch (Exception e) {  
  8.         log.error(sm.getString(  
  9.                 "coyoteConnector.protocolHandlerInstantiationFailed"), e);  
  10.     }  
  11. }  
  12.   
  13. public void setProtocol(String protocol) {  
  14.   
  15.         if (AprLifecycleListener.isAprAvailable()) {  
  16.             if ("HTTP/1.1".equals(protocol)) {  
  17.                 setProtocolHandlerClassName  
  18.                     ("org.apache.coyote.http11.Http11AprProtocol");  
  19.             } else if ("AJP/1.3".equals(protocol)) {  
  20.                 setProtocolHandlerClassName  
  21.                     ("org.apache.coyote.ajp.AjpAprProtocol");  
  22.             } else if (protocol != null) {  
  23.                 setProtocolHandlerClassName(protocol);  
  24.             } else {  
  25.                 setProtocolHandlerClassName  
  26.                     ("org.apache.coyote.http11.Http11AprProtocol");  
  27.             }  
  28.         } else {  
  29.             if ("HTTP/1.1".equals(protocol)) {  
  30.                 setProtocolHandlerClassName  
  31.                     ("org.apache.coyote.http11.Http11Protocol");  
  32.             } else if ("AJP/1.3".equals(protocol)) {  
  33.                 setProtocolHandlerClassName  
  34.                     ("org.apache.coyote.ajp.AjpProtocol");  
  35.             } else if (protocol != null) {  
  36.                 setProtocolHandlerClassName(protocol);  
  37.             }  
  38.         }  
  39.   
  40.     }  

 协议的设置在conf/server.xml中配置,由setProtocol来赋值,tomcat提供了6种协议:

 


 

由上面6个类可知,对于http/ajp协议,tomcat均提供了三种运行模式,及BIO、NIO、APR,BIO即传统的blocking io,性能是最差的,APR是通过安装apr和native,从操作系统层面解决异步IO问题,能大幅提高性能,最省心也能较大幅度提高性能的是NIO,只需将Connector的protocol配成"org.apache.coyote.http11.Http11NioProtocol"即可。

    为了便于分析,这里只分析Http11Protocol。由类图可以看到,归根结底Http11Protocol是ProtocolHandler的实现,在Http11Protocol的构造方法中,对成员变量endpoint和cHandler进行初始化

Java代码   收藏代码
  1. public Http11Protocol() {  
  2.         endpoint = new JIoEndpoint();  
  3.         cHandler = new Http11ConnectionHandler(this);  
  4.         ((JIoEndpoint) endpoint).setHandler(cHandler);  
  5.         setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);  
  6.         setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);  
  7.         setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);  
  8.     }  

 ,这两个很重要,在后面会讲到。

    继续到Connector代码中,由前面提到的tomcat启动过程知道,会调用Connector两个方法init和start。而端口的绑定和监听则分别在这两个方法中完成。

 

    2. Connector的init()方法

    调用的是Connector的initInternal()方法,主要做了3件事

Java代码   收藏代码
  1. protected void initInternal() throws LifecycleException {  
  2.   
  3.         super.initInternal();  
  4.   
  5.         // step 1. Initialize adapter  
  6.         adapter = new CoyoteAdapter(this);  
  7.         protocolHandler.setAdapter(adapter);  
  8.   
  9.         // Make sure parseBodyMethodsSet has a default  
  10.         ifnull == parseBodyMethodsSet ) {  
  11.             setParseBodyMethods(getParseBodyMethods());  
  12.         }  
  13.   
  14.         if (protocolHandler.isAprRequired() &&  
  15.                 !AprLifecycleListener.isAprAvailable()) {  
  16.             //  
  17.         }  
  18.   
  19.         try {  
  20.             // step 2  
  21.             protocolHandler.init();  
  22.         } catch (Exception e) {  
  23.             //  
  24.         }  
  25.   
  26.         // step 3 Initialize mapper listener  
  27.         mapperListener.init();  
  28.     }  

步骤2中Http11Protocol的init方法,最终会调用到其父类AbstractProtocol的init方法,在这个方法里面对endpoint(Http11Protocol使用的是JIoEndPoint)进行了初始化。

Java代码   收藏代码
  1. public void init() throws Exception {  
  2.         // ...  
  3.   
  4.         String endpointName = getName();  
  5.         endpoint.setName(endpointName.substring(1, endpointName.length()-1));  
  6.   
  7.         try {  
  8.             endpoint.init();  
  9.         } catch (Exception ex) {  
  10.             //  
  11.         }  
  12.     }  

 endpoint.init()在AbstractEndpoint中,完成了对需要监听的端口的绑定。

Java代码   收藏代码
  1. public final void init() throws Exception {  
  2.         if (bindOnInit) {  
  3.             bind();  
  4.             bindState = BindState.BOUND_ON_INIT;  
  5.         }  
  6.     }  

 在JIoEndpoint的bind()中完成了对端口的绑定。

 

对于Http11NioProtocol,在NioEndPoint的bind()方法中我们发现了selector,

Java代码   收藏代码
  1. @Override  
  2.     public void bind() throws Exception {  
  3.   
  4.         serverSock = ServerSocketChannel.open();  
  5.         socketProperties.setProperties(serverSock.socket());  
  6.         InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));  
  7.         serverSock.socket().bind(addr,getBacklog());  
  8.         serverSock.configureBlocking(true); //mimic APR behavior  
  9.         serverSock.socket().setSoTimeout(getSocketProperties().getSoTimeout());  
  10.   
  11.         // Initialize thread count defaults for acceptor, poller  
  12.         if (acceptorThreadCount == 0) {  
  13.             // FIXME: Doesn't seem to work that well with multiple accept threads  
  14.             acceptorThreadCount = 1;  
  15.         }  
  16.         if (pollerThreadCount <= 0) {  
  17.             //minimum one poller thread  
  18.             pollerThreadCount = 1;  
  19.         }  
  20.         stopLatch = new CountDownLatch(pollerThreadCount);  
  21.   
  22.         //...  
  23.         selectorPool.open();  
  24.     }  

 而让我们震惊的是,coyote用了自己搞的一个NioSelectorPool,其中maxSelectors=200与普通IO下Acceptor的数量一样。另外,普通IO下用的处理请求的线程池的核心线程数量是10,max是200.当然,这些值可在配置文件里配。

Java代码   收藏代码
  1. public class NioSelectorPool {  
  2.   
  3.     public NioSelectorPool() {  
  4.     }  
  5.   
  6.     private static final Log log = LogFactory.getLog(NioSelectorPool.class);  
  7.   
  8.     protected static final boolean SHARED =  
  9.         Boolean.valueOf(System.getProperty("org.apache.tomcat.util.net.NioSelectorShared""true")).booleanValue();  
  10.   
  11.     protected NioBlockingSelector blockingSelector;  
  12.   
  13.     protected volatile Selector SHARED_SELECTOR;  
  14.   
  15.     protected int maxSelectors = 200;  
  16.     protected long sharedSelectorTimeout = 30000;  
  17.     protected int maxSpareSelectors = -1;  
  18.     protected boolean enabled = true;  
  19.     protected AtomicInteger active = new AtomicInteger(0);  
  20.     protected AtomicInteger spare = new AtomicInteger(0);  
  21.     protected ConcurrentLinkedQueue<Selector> selectors =  
  22.         new ConcurrentLinkedQueue<Selector>();  
  23.     //...  
  24. }  

 

 

三、Connector启动

    Connector的启动会调用start方法,在startInternal方法中,

Java代码   收藏代码
  1. protected void startInternal() throws LifecycleException {  
  2.   
  3.         // Validate settings before starting  
  4.         if (getPort() < 0) {  
  5.             //  
  6.         }  
  7.   
  8.         setState(LifecycleState.STARTING); // 发送STARTING事件  
  9.   
  10.         try {  
  11.             protocolHandler.start(); // 启动端口监听  
  12.         } catch (Exception e) {  
  13.             //  
  14.         }  
  15.   
  16.         mapperListener.start(); // 这个很重要,后面会讲到  
  17.     }  

 可以看到,start相对init是调用了对应的start方法。其中,protocolHandler.start();即调用了Http11Protocol的start方法。最终调用了调用了JIoEndpoint的startInternal方法,初始化了处理连接请求的线程池(默认最大线程数200个),开启Acceptor线程接收请求。

Java代码   收藏代码
  1. public void startInternal() throws Exception {  
  2.   
  3.         if (!running) {  
  4.             running = true;  
  5.             paused = false;  
  6.   
  7.             // Create worker collection  
  8.             if (getExecutor() == null) {  
  9.                 createExecutor();  
  10.             }  
  11.   
  12.             initializeConnectionLatch();  
  13.   
  14.             startAcceptorThreads();  
  15.   
  16.             // Start async timeout thread  
  17.             Thread timeoutThread = new Thread(new AsyncTimeout(),  
  18.                     getName() + "-AsyncTimeout");  
  19.             timeoutThread.setPriority(threadPriority);  
  20.             timeoutThread.setDaemon(true);  
  21.             timeoutThread.start();  
  22.         }  
  23.     }  

    

四、一个http请求在Connector中所经历的代码逻辑

    先来个粗犷的印象,

Code代码   收藏代码
  1. LifecycleBase implements Lifecycle  
  2. start() Lifecycle  
  3.   startInternal() LifecycleBase  
  4.   
  5. ------------------------------  
  6.   
  7. Catalina#load()  
  8.   StandardServer#init()  
  9.     StandService[i]#init()  
  10.       Container[i]#init()  
  11.       Executor[i]#init()  
  12.       Connector[i]#init()  
  13.   
  14. Connector#initInternal()  
  15.   adapter = new CoyoteAdapter(this)  
  16.   AbstractProtocol extends ProtocolHandler.setAdapter(adapter)  
  17.   protocolHandler.init()  
  18.   mapperListener.init()  
  19.   
  20. AbstractProtocol implements ProtocolHandler#init()  
  21.   AbstractEndpoint#init()  
  22.     bind()  
  23.   
  24. ------------------------------  
  25.   
  26. Catalina#start()  
  27.   if (getServer() == null) load();  
  28.   StandardServer#start()  
  29.     StandardService[i]#start()  
  30.       Container#start()  
  31.       Executor[i]#start()  
  32.       Connector[i]#start()  
  33. Connector#startInternal()  
  34.   AbstractProtocol implements ProtocolHandler#start()  
  35.     AbstractEndpoint#start()  
  36.   mapperListener.start()  
  37.   
  38. JIoEndpoint#startInternal()  
  39.   createExecutor()  
  40.   initializeConnectionLatch()  
  41.   startAcceptorThreads()  
  42.   
  43. AbstractEndpoint#startAcceptorThreads()  
  44.   acceptors[i] = createAcceptor()  
  45.   new Thread(acceptors[i]).start()  
  46.   
  47. JIoEndpoint.Acceptor implements Runnable  
  48.   Socket socket = serverSocketFactory.acceptSocket(serverSocket)  
  49.   processSocket(socket)  
  50.     SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket)  
  51.     getExecutor().execute(new SocketProcessor(wrapper))  
  52.   
  53. SocketProcessor implements Runnable  
  54.   state = handler.process(socket,status);  
  55.   
  56. Http11Protocol ... extends AbstractProtocol<S>  
  57.    Http11ConnectionHandler extends AbstractConnectionHandler implements Handler  
  58. AbstractProtocol  
  59.    AbstractConnectionHandler implements AbstractEndpoint.Handler  
  60.       SocketState process(SocketWrapper<S> wrapper, SocketStatus status)  
  61.         state = processor.process(wrapper)  
  62.   
  63. AbstractHttp11Processor  
  64.   SocketState process(SocketWrapper<S> socketWrapper)  
  65.     adapter.service(request, response)  
  66.     

 在上文中有分析Connector在启动的时候会监听端口。继续以JIoEndpoint为例,在其Accptor类中:

Java代码   收藏代码
  1. protected class Acceptor extends AbstractEndpoint.Acceptor {  
  2.     @Override  
  3.     public void run() {  
  4.         while (running) {  
  5.             //  
  6.             try {  
  7.                 //当前连接数  
  8.                 countUpOrAwaitConnection();  
  9.                 Socket socket = null;  
  10.                 try {  
  11.                     //取出队列中的连接请求  
  12.                     socket = serverSocketFactory.acceptSocket(serverSocket);  
  13.                 } catch (IOException ioe) {  
  14.                     countDownConnection();  
  15.                 }  
  16.                 if (running && !paused && setSocketOptions(socket)) {  
  17.                     //处理请求  
  18.                     if (!processSocket(socket)) {  
  19.                         countDownConnection();  
  20.                         closeSocket(socket);  
  21.                     }  
  22.                 } else {  
  23.                     countDownConnection();  
  24.                     // Close socket right away  
  25.                     closeSocket(socket);  
  26.                 }  
  27.             }   
  28.             //  
  29.         }  
  30.     }  
  31. }  

 在上面的代码中,socket = serverSocketFactory.acceptSocket(serverSocket);与客户端建立连接,将连接的socket交给processSocket(socket)来处理。在processSocket中,对socket进行包装一下交给线程池来处理:

Java代码   收藏代码
  1. protected boolean processSocket(Socket socket) {  
  2.     try {  
  3.         SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);  
  4.         wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());  
  5.         wrapper.setSecure(isSSLEnabled());  
  6.         //交给线程池处理连接  
  7.         getExecutor().execute(new SocketProcessor(wrapper));  
  8.     }   
  9.     //  
  10.     return true;  
  11. }  

 线程池处理的任务SocketProccessor,通过代码分析:

Java代码   收藏代码
  1. protected class SocketProcessor implements Runnable {  
  2.   
  3.     protected SocketWrapper<Socket> socket = null;  
  4.     protected SocketStatus status = null;  
  5.   
  6.     @Override  
  7.     public void run() {  
  8.         boolean launch = false;  
  9.         synchronized (socket) {  
  10.             SocketState state = SocketState.OPEN;  
  11.             try {  
  12.                 serverSocketFactory.handshake(socket.getSocket());  
  13.             }   
  14.             //  
  15.             if ((state != SocketState.CLOSED)) {  
  16.                 //委派给Handler来处理  
  17.                 if (status == null) {  
  18.                     state = handler.process(socket, SocketStatus.OPEN_READ);  
  19.                 } else {  
  20.                     state = handler.process(socket,status);  
  21.                 }  
  22.             }}}  
  23.             //  
  24. }  

 即在SocketProcessor中,将Socket交给handler处理,这个handler就是在Http11Protocol的构造方法中赋值的Http11ConnectionHandler,在该类的父类process方法中通过请求的状态,来创建Http11Processor处理器进行相应的处理,切到Http11Proccessor的父类AbstractHttp11Proccessor中。

Java代码   收藏代码
  1. public SocketState process(SocketWrapper socketWrapper) {  
  2.     RequestInfo rp = request.getRequestProcessor();  
  3.     rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);  
  4.   
  5.     // Setting up the I/O  
  6.     setSocketWrapper(socketWrapper);  
  7.     getInputBuffer().init(socketWrapper, endpoint);  
  8.     getOutputBuffer().init(socketWrapper, endpoint);  
  9.   
  10.     while (!getErrorState().isError() && keepAlive && !comet && !isAsync() &&  
  11.             upgradeInbound == null &&  
  12.             httpUpgradeHandler == null && !endpoint.isPaused()) {  
  13.         //  
  14.         if (!getErrorState().isError()) {  
  15.             // Setting up filters, and parse some request headers  
  16.             rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);  
  17.             try {  
  18.                 //请求预处理  
  19.                 prepareRequest();  
  20.             }   
  21.             //  
  22.         }  
  23.         //  
  24.         if (!getErrorState().isError()) {  
  25.             try {  
  26.                 rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);  
  27.                 //交由适配器处理  
  28.                 adapter.service(request, response);  
  29.   
  30.                 if(keepAlive && !getErrorState().isError() && (  
  31.                         response.getErrorException() != null ||  
  32.                                 (!isAsync() &&  
  33.                                 statusDropsConnection(response.getStatus())))) {  
  34.                     setErrorState(ErrorState.CLOSE_CLEAN, null);  
  35.                 }  
  36.                 setCometTimeouts(socketWrapper);  
  37.             }   
  38.         }  
  39.     }  
  40.     //  
  41. }            

代码很长,删掉的部分比较多,在这个方法里,可以看到Request和Response的生成,从Socket中获取请求数据,keep-alive处理,数据包装等,最后交给了CoyoteAdapter的service方法。



原文链接:[http://wely.iteye.com/blog/2295171]

相关文章
|
2月前
|
监控 Java 应用服务中间件
Spring Boot整合Tomcat底层源码分析
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置和起步依赖等特性,大大简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是其与Tomcat的整合。
62 1
|
4月前
|
监控 网络协议 应用服务中间件
【Tomcat源码分析】从零开始理解 HTTP 请求处理 (第一篇)
本文详细解析了Tomcat架构中复杂的`Connector`组件。作为客户端与服务器间沟通的桥梁,`Connector`负责接收请求、封装为`Request`和`Response`对象,并传递给`Container`处理。文章通过四个关键问题逐步剖析了`Connector`的工作原理,并深入探讨了其构造方法、`init()`与`start()`方法。通过分析`ProtocolHandler`、`Endpoint`等核心组件,揭示了`Connector`初始化及启动的全过程。本文适合希望深入了解Tomcat内部机制的读者。欢迎关注并点赞,持续更新中。如有问题,可搜索【码上遇见你】交流。
【Tomcat源码分析】从零开始理解 HTTP 请求处理 (第一篇)
|
4月前
|
人工智能 前端开发 Java
【Tomcat源码分析】启动过程深度解析 (二)
本文深入探讨了Tomcat启动Web应用的过程,重点解析了其加载ServletContextListener及Servlet的机制。文章从Bootstrap反射调用Catalina的start方法开始,逐步介绍了StandardServer、StandardService、StandardEngine、StandardHost、StandardContext和StandardWrapper的启动流程。每个组件通过Lifecycle接口协调启动,子容器逐层启动,直至整个服务器完全启动。此外,还详细分析了Pipeline及其Valve组件的作用,展示了Tomcat内部组件间的协作机制。
【Tomcat源码分析】启动过程深度解析 (二)
|
4月前
|
前端开发 Java 应用服务中间件
【Tomcat源码分析 】"深入探索:Tomcat 类加载机制揭秘"
本文详细介绍了Java类加载机制及其在Tomcat中的应用。首先回顾了Java默认的类加载器,包括启动类加载器、扩展类加载器和应用程序类加载器,并解释了双亲委派模型的工作原理及其重要性。接着,文章分析了Tomcat为何不能使用默认类加载机制,因为它需要解决多个应用程序共存时的类库版本冲突、资源共享、类库隔离及JSP文件热更新等问题。最后,详细展示了Tomcat独特的类加载器设计,包括Common、Catalina、Shared、WebApp和Jsp类加载器,确保了系统的稳定性和安全性。通过这种设计,Tomcat实现了不同应用程序间的类库隔离与共享,同时支持JSP文件的热插拔。
【Tomcat源码分析 】"深入探索:Tomcat 类加载机制揭秘"
|
4月前
|
设计模式 应用服务中间件 容器
【Tomcat源码分析】Pipeline 与 Valve 的秘密花园
本文深入剖析了Tomcat中的Pipeline和Valve组件。Valve作为请求处理链中的核心组件,通过接口定义了关键方法;ValveBase为其基类,提供了通用实现。Pipeline则作为Valve容器,通过首尾相连的Valve链完成业务处理。StandardPipeline实现了Pipeline接口,提供了详细的Valve管理逻辑。通过对代码的详细分析,揭示了模板方法模式和责任链模式的应用,展示了系统的扩展性和模块化设计。
【Tomcat源码分析】Pipeline 与 Valve 的秘密花园
|
4月前
|
设计模式 人工智能 安全
【Tomcat源码分析】生命周期机制 Lifecycle
Tomcat内部通过各种组件协同工作,构建了一个复杂的Web服务器架构。其中,`Lifecycle`机制作为核心,管理组件从创建到销毁的整个生命周期。本文详细解析了Lifecycle的工作原理及其方法,如初始化、启动、停止和销毁等关键步骤,并展示了LifecycleBase类如何通过状态机和模板模式实现这一过程。通过深入理解Lifecycle,我们可以更好地掌握组件生命周期管理,提升系统设计能力。欢迎关注【码上遇见你】获取更多信息,或搜索【AI贝塔】体验免费的Chat GPT。希望本章内容对你有所帮助。
|
5月前
|
网络协议 Java 应用服务中间件
Tomcat源码分析 (一)----- 手撕Java Web服务器需要准备哪些工作
本文探讨了后端开发中Web服务器的重要性,特别是Tomcat框架的地位与作用。通过解析Tomcat的内部机制,文章引导读者理解其复杂性,并提出了一种实践方式——手工构建简易Web服务器,以此加深对Web服务器运作原理的认识。文章还详细介绍了HTTP协议的工作流程,包括请求与响应的具体格式,并通过Socket编程在Java中的应用实例,展示了客户端与服务器间的数据交换过程。最后,通过一个简单的Java Web服务器实现案例,说明了如何处理HTTP请求及响应,强调虽然构建基本的Web服务器相对直接,但诸如Tomcat这样的成熟框架提供了更为丰富和必要的功能。
|
8月前
|
前端开发 Java 应用服务中间件
|
8月前
|
Java 应用服务中间件
Springboot启动的时候初始化的线程池默认配置tomcat
Springboot启动的时候初始化的线程池默认配置tomcat
196 1