5 Connector 分析
Connector 用于接收请求并将请求封装成Request 和Response 来具体处理,最底层是使用Socket 来进行连接的, Request 和Response 是按照HTTP 协议来封装的,所以Connector 同时实现了TCP/IP 协议和HTTP 协议, Request 和Response 封装完之后交给Container 进行处理,Container 就是Servlet 的容器, Container 处理完之后返回给Connector,最后Connector 使用Socket 将处理结果返回给客户端,这样整个请求就处理完了。
5.1 Connector 的结构
Connector 中具体是用ProtocolHandler 来处理请求的,不同的ProtocolHandler 代表不同的连接类型,比如, Http11Protocol 使用的是普通Socket 来连接的, Http 11 NioProtocol 使用的是NioSocket 来连接的。
ProtocolHandler 里面有3 个非常重要的组件: Endpoint 、Processor 和Adapter。
- Endpoint用于处理底层Socket 的网络连接,
- Processor 用于将Endpoint 接收到的Socket 封装成Request,
- Adapter 用于将封装好的Request 交给Container 进行具体处理。
也就是说Endpoint用来实现TCP/IP 协议, Processor 用来实现HTTP 协议, Adapter 将请求适配到Servlet 容器进行具体处理。
Endpoint 的抽象实现AbstractEndpoint 里面定义的Acceptor 和AsyncTimeout 两个内部类和一个Handler 接口。Acceptor 用于监昕请求, AsyncTimeout 用于检查异步request 的超时,Handler 用于处理接收到的Socket,在内部调用了Processor 进行处理。
Connector 的结构如下:
5.2 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();
}
- 在initlntemal 方法中首先新建了一个Adapter 并设置到ProtocolHandler 中,然后对ProtocolHandler 进行初始化;
- 在startIntemal 方法中首先判断设置的端口是否小于0 ,如果小于0 就抛出异常,开则就调用ProtocolHandler 的start 方法来启动;
- 在stoplntemal 方法巾先设置了生命周期状态,然后调用了ProtocoIHandler 的stop 方法;
- 在destroylntemal 方法中除了调用ProtocoIHandler 的destroy 方法,还会将当前的Connector 从Service 中删除井调用父类的destroyInternal 方法。
5.3 ProtocolHandler
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 的生命周期方法。
5.4 处理TCP/IP 协议的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>类型, 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 线程来接收请求。
5.5 处理HTTP 协议的Processor
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 中,最后对处理的结果进行了处理,如有没有启动异步处理、处理过程巾有没有抛出异常等。
5.6 适配器Adapter
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。