连接器是tomcat核心组件之一,负责处理外部连接响应目前连接器支持HTTP/1.1、HTTP/2、AJP三种协议,支持NIO、NIO2、APR三种IO模型。连接器与另一核心组件容器构成service组件,通过配置多个service可实现不同协议不同端口访问。
连接器主要工作流程可分为以下几步:
1.监听端口,接受请求获取网络字节流。
2.解析字节流,生成tomcat request对象。
3.将tomcat request对象转为servlet request对象。
4.调用servlet容器。
5.将返回的servlet response转为tomcat response。
6.tomcat response 转为网络字节流
7.返回客户端。
在tomcat中这些请求由三个组件endpoint、processor 和 adapter实现。其中endpoint负责监听端口,对客户端请求/响应进行处理。processor网络字节流与tomcat对象之间转换。adapter是典型的适配器模式负责将不同协议的tomcat对象与servlet对象间转换,然后调用容器进行处理。tomcat使用这三个组件进行解耦,并达到IO模型与通讯协议自由组合的效果。
下面通过源码验证以上推论以HTTP/1.1协议,NIO模型为例。
Connector类为入口,创建不同ProtocolHandler
public Connector(String protocol) {
boolean apr = AprStatus.getUseAprConnector() && AprStatus.isInstanceCreated()
&& AprLifecycleListener.isAprAvailable();
ProtocolHandler p = null;
try {
p = ProtocolHandler.create(protocol, apr);
} catch (Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
}
if (p != null) {
protocolHandler = p;
protocolHandlerClassName = protocolHandler.getClass().getName();
} else {
protocolHandler = null;
protocolHandlerClassName = protocol;
}
// Default for Connector depends on this system property
setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));
}
ProtocolHandler.create(protocol, apr)通过指定的protocol创建不同协议与io模型组合处理器
public static ProtocolHandler create(String protocol, boolean apr)
throws ClassNotFoundException, InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
if (protocol == null || "HTTP/1.1".equals(protocol)
|| (!apr && org.apache.coyote.http11.Http11NioProtocol.class.getName().equals(protocol))
|| (apr && org.apache.coyote.http11.Http11AprProtocol.class.getName().equals(protocol))) {
if (apr) {
return new org.apache.coyote.http11.Http11AprProtocol();
} else {
return new org.apache.coyote.http11.Http11NioProtocol();
}
} else if ("AJP/1.3".equals(protocol)
|| (!apr && org.apache.coyote.ajp.AjpNioProtocol.class.getName().equals(protocol))
|| (apr && org.apache.coyote.ajp.AjpAprProtocol.class.getName().equals(protocol))) {
if (apr) {
return new org.apache.coyote.ajp.AjpAprProtocol();
} else {
return new org.apache.coyote.ajp.AjpNioProtocol();
}
} else {
// Instantiate protocol handler
Class<?> clazz = Class.forName(protocol);
return (ProtocolHandler) clazz.getConstructor().newInstance();
}
}
进入Http11NioProtocol,Http11NioProtocol在初始化是创建NioEndpoint()对象
public Http11NioProtocol() {
super(new NioEndpoint());
}
首先关注NioEndpoint中bind方法
/**
* Initialize the endpoint.
*/
@Override
public void bind() throws Exception {
// 该方法创建完成端口监听
initServerSocket();
setStopLatch(new CountDownLatch(1));
// Initialize SSL if needed
initialiseSsl();
}
protected void initServerSocket() throws Exception {
// 截取部分代码
serverSock = ServerSocketChannel.open();
socketProperties.setProperties(serverSock.socket());
InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
// ServerSocketChannel绑定对应端口,通过getAcceptCount设置队列长度,当应用程序处理不过来时
// 请求会先放入队列中
serverSock.bind(addr, getAcceptCount());
}
// 设置为阻塞式通过accept()接受请求,并将请求翻入Poller的队列中
serverSock.configureBlocking(true); //mimic APR behavior
}
Poller实现Runnable,会不断的查询是否有请求进入队列。当获取到队列中最新请求会调用processor组件进行处理
@Override
public void run() {
// Loop until destroy() is called
while (true) {
if (!close) {
hasEvents = events();
if (wakeupCounter.getAndSet(-1) > 0) {
// If we are here, means we have other stuff to do
// Do a non blocking select
keyCount = selector.selectNow();
} else {
keyCount = selector.select(selectorTimeout);
}
while (iterator != null && iterator.hasNext()) {
SelectionKey sk = iterator.next();
iterator.remove();
NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
// Attachment may be null if another thread has called
// cancelledKey()
if (socketWrapper != null) {
// 执行处理
processKey(sk, socketWrapper);
}
}
}
processor将请求放入线程池中
protected void processKey(SelectionKey sk, NioSocketWrapper socketWrapper) {
if (sk.isReadable()) {
if (socketWrapper.readOperation != null) {
if (!socketWrapper.readOperation.process()) {
closeSocket = true;
}
} else if (socketWrapper.readBlocking) {
synchronized (socketWrapper.readLock) {
socketWrapper.readBlocking = false;
socketWrapper.readLock.notify();
}
} else if (!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) {
closeSocket = true;
}
}
if (!closeSocket && sk.isWritable()) {
if (socketWrapper.writeOperation != null) {
if (!socketWrapper.writeOperation.process()) {
closeSocket = true;
}
} else if (socketWrapper.writeBlocking) {
synchronized (socketWrapper.writeLock) {
socketWrapper.writeBlocking = false;
socketWrapper.writeLock.notify();
}
} else if (!processSocket(socketWrapper, SocketEvent.OPEN_WRITE, true)) {
closeSocket = true;
}
}
}
线程池调用NioEndpoint中doRun执行任务,在执行任务时使用适配器将不同tomcat request转为servlet request并调用容器进行处理。