Connector 用于接收请求并将请求封装成Request 和Response 来具体处理,最底层是使用Socket 来进行连接的, Request 和Response 是按照HTTP 协议来封装的,所以Connector 同时实现了TCP/IP 协议和HTTP 协议, Request 和Response 封装完之后交给Container 进行处理,Container 就是Servlet 的容器, Container 处理完之后返回给Connector,最后Connector 使用Socket 将处理结果返回给客户端,这样整个请求就处理完了。
Connector 中具体是用ProtocolHandler 来处理请求的,不同的ProtocolHandler 代表不同的连接类型,比如, Http11Protocol 使用的是普通Socket 来连接的, Http 11 NioProtocol 使用的是NioSocket 来连接的。 ProtocolHandler 里面有3 个非常重要的组件: Endpoint 、Processor 和Adapter。
也就是说Endpoint用来实现TCP/IP 协议, Processor 用来实现HTTP 协议, Adapter 将请求适配到Servlet 容器进行具体处理。 Endpoint 的抽象实现AbstractEndpoint 里面定义的Acceptor 和AsyncTimeout 两个内部类和一个Handler 接口。Acceptor 用于监昕请求, AsyncTimeout 用于检查异步request 的超时,Handler 用于处理接收到的Socket,在内部调用了Processor 进行处理。 Connector 的结构如下:
Connector 类本身的作用主要是在其创建时创建ProtocolHandler,然后在生命周期的相关方法中调用了ProtocolHandler 的相关生命周期方法。Connector 的使用方法是通过Connector 标签配置在conf/server.xml 文件中,所以Connector 是在Catalina 的load 方法中根据conf/server.xml 配置文件创建Server对象时创建的。Connector 的生命周期方法是在Service 中调用的。
Connector 的创建 Connector 的创建过程主要是初始化ProtocolHandler。server.xrnl 配置文件中Connector 标签的protocol 属性会设置到Connector 构造函数的参数中,它用于指定ProtocolHandler 的类型, Connector 的构造函数代码如下:
public Connector(String protocol) { setProtocol(protocol); // Instantiate protocol handler try { Class<?> clazz = Class.forName(protocolHandlerClassName); this.protocolHandler = (ProtocolHandler) clazz.newInstance(); } catch (Exception e) { log.error(sm.getString( "coyoteConnector.protocolHandlerInstantiationFailed"), e); } }
这里首先根据传人的protocol 参数调用setProtocol 方法设置了protocolHandlet℃lassName 属性,接着用protoco!HandlerClassName 所代表的类创建了Protoco旧andI er 并赋值给了protocolHandler属性。 设置protoco!HandlerClassName 属性的setProtocol 方法代码如下:
/** * 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); } } }
Apr 是Apache Portable Runtime 的缩写,是Apache 提供的一个运行时环境,如果要使用Apr 需要先安装,安装后Tomcat 可以自己检测出来。如果安装了Apr, setProtocol 方法会根据配置的HTTP/1.1 属性对应地将protocolHandlerClassName 设置为org.apache.coyote.http11.Http11.AprProtocol ,如果没有安装Apr,会根据配置的HTTP/1.1 属性将protocoHandlerClassName设置为com..apache.coyote.http11.Http11NioProtocol,然后就会根据protocolHandlerClassName 来创建ProtocolHandler。
Connector 生命周期处理方法 Connector 的生命周期处理方法中主要调用了ProtocolHandler 的相应生命周期方法,代码如下:
@Override protected void initInternal() throws LifecycleException { super.initInternal(); // Initialize adapter adapter = new CoyoteAdapter(this); protocolHandler.setAdapter(adapter); // Make sure parseBodyMethodsSet has a default if( null == parseBodyMethodsSet ) { setParseBodyMethods(getParseBodyMethods()); } if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) { throw new LifecycleException( sm.getString("coyoteConnector.protocolHandlerNoApr", getProtocolHandlerClassName())); } try { protocolHandler.init(); } catch (Exception e) { throw new LifecycleException (sm.getString ("coyoteConnector.protocolHandlerInitializationFailed"), e); } // Initialize mapper listener mapperListener.init(); } @Override protected void startInternal() throws LifecycleException { // Validate settings before starting if (getPort() < 0) { throw new LifecycleException(sm.getString( "coyoteConnector.invalidPort", Integer.valueOf(getPort()))); } 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(); } @Override protected void stopInternal() throws LifecycleException { setState(LifecycleState.STOPPING); try { protocolHandler.stop(); } catch (Exception e) { throw new LifecycleException (sm.getString ("coyoteConnector.protocolHandlerStopFailed"), e); } mapperListener.stop(); } @Override protected void destroyInternal() throws LifecycleException { mapperListener.destroy(); try { protocolHandler.destroy(); } catch (Exception e) { throw new LifecycleException (sm.getString ("coyoteConnector.protocolHandlerDestroyFailed"), e); } if (getService() != null) { getService().removeConnector(this); } super.destroyInternal(); }
Tomcat 中ProtocolHandler 的继承结构如图 ProtocolHandler 有一个抽象实现类AbstractProtocol, AbstractProtocol 下面分了三种类型: Ajp 、HTTP 和Spdy 。Ajp是Apache JServ Protocol 的缩写, Apache 的定向包协议,主要用于与前端服务器(如Apache )进行通信,它是长连接,不需要每次通信都重新建立连接,这样就节省了开销; Spdy 协议Google开发的协议,作用类似HTTP ,比HTTP 效率高,不过这只是Google 制定的企业级协议,使用并不广泛,而且在HTTP/2 协议中已经包含了Spdy 所提供的优势,所以Spdy 协议平常很少使用,不过Tomcat 提供了支持。 这里的ProtocolHandler 以默认配置中的org.apache.coyote.http11 .Http11.NioProtocol 为例来分析,它使用HTTP11协议, TCP 层使用NioSocket 来传输数据。 Http11NioProtocol 的构造雨数中创建了NioEndpoint 类型的Endpoint,井新建了Http11ConnectionHandler 类型的Handler 然后设置到了Endpoint 中,代码如下:
public Http11NioProtocol() { endpoint=new NioEndpoint(); cHandler = new Http11ConnectionHandler(this); ((NioEndpoint) endpoint).setHandler(cHandler); setSoLinger(Constants.DEFAULT_CONNECTION_LINGER); setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT); setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY); }
四个生命周期方法是在父类AbstractProtocol 中实现的,其中主要调用了Endpoint 的生命周期方法。
Endpoint 用于处理具体连接和传输数据, NioEndpoint 继承自org.apache.tomcat. util.net.AbstractEndpoint,在NioEndpoint 中新增了Poller 和SocketProcessor 内部类, NioEndpoint 中处理请求的具体流程如图: NioEndpoint 的init 和start 方法在父类AbstractEndpoin 中,代码如下:
public final void init() throws Exception { testServerCipherSuitesOrderSupport(); if (bindOnInit) { bind(); bindState = BindState.BOUND_ON_INIT; } } public final void start() throws Exception { if (bindState == BindState.UNBOUND) { bind(); bindState = BindState.BOUND_ON_START; } startInternal(); }
这两个方法主要调用bind 和startlntemal 方法,它们是模板方法,在NioEndpoint 中实现, bind 方法代码如下:
/** * Initialize the endpoint. */ @Override public void bind() throws Exception { serverSock = ServerSocketChannel.open(); socketProperties.setProperties(serverSock.socket()); InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort())); serverSock.socket().bind(addr,getBacklog()); serverSock.configureBlocking(true); //mimic APR behavior serverSock.socket().setSoTimeout(getSocketProperties().getSoTimeout()); // Initialize thread count defaults for acceptor, poller if (acceptorThreadCount == 0) { // FIXME: Doesn't seem to work that well with multiple accept threads acceptorThreadCount = 1; } if (pollerThreadCount <= 0) { //minimum one poller thread pollerThreadCount = 1; } stopLatch = new CountDownLatch(pollerThreadCount); // Initialize SSL if needed if (isSSLEnabled()) { SSLUtil sslUtil = handler.getSslImplementation().getSSLUtil(this); sslContext = sslUtil.createSSLContext(); sslContext.init(wrap(sslUtil.getKeyManagers()), sslUtil.getTrustManagers(), null); SSLSessionContext sessionContext = sslContext.getServerSessionContext(); if (sessionContext != null) { sslUtil.configureSessionContext(sessionContext); } // Determine which cipher suites and protocols to enable enabledCiphers = sslUtil.getEnableableCiphers(sslContext); enabledProtocols = sslUtil.getEnableableProtocols(sslContext); } if (oomParachute>0) reclaimParachute(true); selectorPool.open(); }
这里的bind 方法中首先初始化了ServerSocketChannel,然后检查了代表Acceptor 和Poller 初始化的线程数量的acceptor’threadCount 属性和pollerηtreadCount 属性,它们至少为1, Acceptor 用于接收请求,接收到请求后交给Poller 处理,它们都是启动线程来处理的。另外还处理了初始化SSL 等内容。NioEndpoint 的startlntemal 方法代码如下:
@Override public void startInternal() throws Exception { if (!running) { running = true; paused = false; // Create worker collection if ( getExecutor() == null ) { createExecutor(); } initializeConnectionLatch(); // Start poller threads pollers = new Poller[getPollerThreadCount()]; for (int i=0; i<pollers.length; i++) { pollers[i] = new Poller(); Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i); pollerThread.setPriority(threadPriority); pollerThread.setDaemon(true); pollerThread.start(); } startAcceptorThreads(); } }
这里首先初始化了一些属性, 然后启动了Poller 和Acceptor 来处理请求,初始化的属性中的processorCache 属性是SynchronizedStack类型, SocketProcessor 并不是前面介绍的Processor ,而是NioEndpoint 的一个内部类, Poller 接收到请求后就会交给它处理, SocketProcessor 又会将请求传递到Handler。 启动Acceptor 的startAcceptorThreads 方法在AbstractEndpoint 中,代码如下:
protected final void startAcceptorThreads() { int count = getAcceptorThreadCount(); acceptors = new Acceptor[count]; for (int i = 0; i < count; i++) { acceptors[i] = createAcceptor(); String threadName = getName() + "-Acceptor-" + i; acceptors[i].setThreadName(threadName); Thread t = new Thread(acceptors[i], threadName); t.setPriority(getAcceptorThreadPriority()); t.setDaemon(getDaemon()); t.start(); } }
这里的getAcceptorThreadCount 方法就是获取的init 方法中处理过的acceptorThreadCount属性,获取到后就会启动相应数量的Acceptor 线程来接收请求。
Processor 用于处理应用层协议(如HTTP ),它的继承结构如图: Processor 有两个AbstractProtocol 抽象继承类。正常处理协议使用的是下面的AbstractProtocol 及其实现类, 上面的AbstractProtocol 是Servlet3. 1之后才新增的,用于处理HTTP 的升级协议, 当正常(下面) 的Processor 处理之后如果Socket 的状态是UPGADING ,那么Endpoint 中的Handler 将会接着创建并调用org.apache.coyote.http11. upgrade 包中的Processor 进行处理,这里的HTTP 升级协议指的是WebSocket 协议。 具体实现应用层协议处理请求的是AbstractAjpProcessor 和AbstractHttp11Processor 中的process方法,这个方法中首先封装了Request 和Response , 然后调用Adapter 将请求传递到了Container 中,最后对处理的结果进行了处理,如有没有启动异步处理、处理过程巾有没有抛出异常等。
Adapter 只有一个实现类,那就是org.apache.catalina.connector 包下的CoyoteAdapter 类。Processor 在其process 方法中会调用Adapter 的service 方法来处理请求, Adapter 的service 方法主要是调用Container 管道中的invoke方法来处理请求,在处理之前对Request和Response做了处理,将原来创建的org.apache.coyote 包下的Request 和Response 封装成了org.apache.catal ina.connector 的Request 和Response ,并在处理完成后判断再启动了Comet(长连接推模式)和是否启动了异步请求,并作出相应处理。调用Container 管道的相应代码片段如下:
connector.getService().getContainer().getPipeline().getFirst().invoke(request,response);
这里首先从Connector 中获取到Service ( Connector 在initInternal 方法中创建CoyoteAdapter的时候已经将自己设置到了CoyoteAdapter 中),然后从Service 中获取Container ,接着获取管道,再获取管道的第一个Value,最后调用invoke 方法执行请求。Service 中保存的是最顶层的容器,当调用最顶层容器管道的invoke 方法时,管道将逐层调用各层容器的管道中Value 的invoke 方法,直到最后调用Wrapper 的管道中的BaseValue ( StandardWrapperValve)来处理Filter 和Servlet。
原文链接:https://blog.csdn.net/gchd19921992/article/details/79076926