0%

tomcat之Connector连接器

Connector连接器

Coyote是Tomcat连接器框架的名称,是Tomcat服务器提供的供客户端访问的外部接口。客户端通过Coyote与服务器建立连接、发送请求并接收响应。

Connector封装了底层的网络通信(Socket请求和响应),为Catalina容器提供了统一的接口,使Catalina容器与具体的请求协议及I/O方式解耦。Connector将Socket输入转换为Request对象,交由Catalina容器进行处理,处理完成后,Catalina通过Connector提供的Response对象将结果写入到输出流

Connector就是ServerSocket来等待HTTP请求,Processor解析HTTP请求来创建HttpRequest和HttpResponse

Connector中,Tomcat支持3种传输协议

  • HTTP/1.1 主要用于Tomcat单独运行(不与Web服务器集成)的情况

  • AJP协议 用于和Web服务器集成,以实现针对静态资源的优化以及集群部署

  • HTTP/2.0 新的HTTP协议(Tomcat8.5之后开始支持)

针对HTTP和AJP协议,Connector又按照I/O方式提供了几种方案(8.5之后移除了对BIO的支持)

  • NIO 采用Java NIO类库实现
  • NIO2 采用JDK7中最新的NIO2实现
  • APR 采用APR实现。是使用C/C++编写的本地库,如果选择该方案,需要单独安装APR库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8" relaxedPathChars="|{}[],%"
relaxedQueryChars="|{}[],%"
maxThreads="500" // 最大线程数
minSpareThreads="100" // 初始化时创建的线程数
maxSpareThreads="200" // 一旦超过该值,tomcat会关闭不再需要的socket线程
acceptCount="200" // 当所有可以使用的线程数都被使用时,可以放入处理队列中的请求数
enableLookups="false" // 是否反查域名
maxHttpHeaderSize="8192" // http请求头的最大长度
disableUploadTimeout="true" // 上传是否使用超时机制
compression="on" // 是否压缩
compressionOnMinSize="10240" // 启用压缩内容的大小
noCompressionUserAgents="gozilla,traviata" // 哪些浏览器不启用压缩
commpressableMimeType="text/html,test/xml,application/json" // 哪些资源需要压缩

/>

Connector连接器功能

  • 监听网络端口
  • 接受网络连接请求
  • 读取请求字节流
  • 根据HTTP/AJP协议解析字节流,生成统一的Tomcat Request对象
  • 将Tomcat Request对象转成标准的Servlet Request对象
  • 调用Servlet容器,得到ServletResponse对象
  • 将ServletResponse转成Tomcat Response对象
  • 将Tomcat Response对象转成字节流
  • 将响应字节流返回浏览器

Connector连接器结构

上述功能简化一下就是网络通信、协议解析以及Tomcat定义的对象与标准对象的转化,分别对应了三个组件,Endpoint、Processor和Adapter

Connector结构

ProtocolHandler

Connector协议接口,通过封装Endpoint、Processor和Adapter,实现针对具体协议的处理功能。使用ProtocolHandler来处理请求,不同的ProtocolHandler表示不同的连接类型,如Http11NioProtocol、Http11AprProtocol等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Connector中设置协议
public void setProtocol(String protocol) {
// 判断是否启用APR
boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
AprLifecycleListener.getUseAprConnector();

if ("HTTP/1.1".equals(protocol) || protocol == null) {
if (aprConnector) {
setProtocolHandlerClassName("org.apache.coyote.http11.Http11AprProtocol");
} else {
setProtocolHandlerClassName("org.apache.coyote.http11.Http11NioProtocol");
}
} else if ("AJP/1.3".equals(protocol)) {
if (aprConnector) {
setProtocolHandlerClassName("org.apache.coyote.ajp.AjpAprProtocol");
} else {
setProtocolHandlerClassName("org.apache.coyote.ajp.AjpNioProtocol");
}
} else {
setProtocolHandlerClassName(protocol);
}
}

根据不同的协议来实例化不同的ProtocolHandler对象

1
2
3
4
5
6
7
8
9
10
ProtocolHandler p = null;
try {
Class<?> clazz = Class.forName(protocolHandlerClassName);
p = (ProtocolHandler) clazz.getConstructor().newInstance();
} catch (Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
} finally {
this.protocolHandler = p;
}
Endpoint

Connector通信端点,即通信监听的接口,是具体的Socket接收处理类,是对传输层的抽象,用来处理底层Socket的网络连接,提供字节流给Processor。AbstractEndpoint抽象类,对于不同的I/O方式有不同的子类,NioEndpoint(NIO)、AprEndpoint(APR)以及Nio2Endpoint,相当于实现了TCP/IP协议

  • AprEndpoint:对应的是APR模式,简单理解就是从操作系统级别解决异步IO的问题,大幅度提高服务器的处理和响应性能。但是启用这种模式需要安装一些其他的依赖库
  • Nio2Endpoint:异步IO
  • NioEndpoint:NIO实现了非阻塞IO,Tomcat默认启动是以这个来启动的

Acceptor 用于监听 Socket 连接请求,调用accept方法阻塞接收建立的连接,封装为PollerEvent,加入队列中,Poller会调用processSocket交给SocketProcessor处理socket。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public void run() {

int errorDelay = 0;

// Loop until we receive a shutdown command
while (running) {

state = AcceptorState.RUNNING;

try {


SocketChannel socket = null;
try {
// Accept the next incoming connection from the server
// socket
// 接收accept
socket = serverSock.accept();
} catch (IOException ioe) {
// We didn't get a socket
countDownConnection();
if (running) {
// Introduce delay if necessary
errorDelay = handleExceptionWithDelay(errorDelay);
// re-throw
throw ioe;
} else {
break;
}
}
// Successful accept, reset the error delay
errorDelay = 0;

// Configure the socket
if (running && !paused) {
// setSocketOptions() will hand the socket off to
// an appropriate processor if successful
// 封装为PollerEvent,加入队列中,Poller会调用processSocket交给SocketProcessor处理socket
if (!setSocketOptions(socket)) {
closeSocket(socket);
}
} else {
closeSocket(socket);
}
} catch (Throwable t) {
}
}
state = AcceptorState.ENDED;
}

Endpoint 接收到 socket 连接后,生成一个 socketProcessor 交给线程池处理,run 方法会调用 Processor 解析应用层协议。SocketProcessor用于处理Acceptor接收到的Socket请求,其实现了Runnable接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
protected void doRun() {
NioChannel socket = socketWrapper.getSocket();
SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());

try {
int handshake = -1;

try {
if (key != null) {
if (socket.isHandshakeComplete()) {
// No TLS handshaking required. Let the handler
// process this socket / event combination.
handshake = 0;
} else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
event == SocketEvent.ERROR) {
// Unable to complete the TLS handshake. Treat it as
// if the handshake failed.
handshake = -1;
} else {
handshake = socket.handshake(key.isReadable(), key.isWritable());
// The handshake process reads/writes from/to the
// socket. status may therefore be OPEN_WRITE once
// the handshake completes. However, the handshake
// happens when the socket is opened so the status
// must always be OPEN_READ after it completes. It
// is OK to always set this as it is only used if
// the handshake completes.
event = SocketEvent.OPEN_READ;
}
}
} catch (IOException x) {
handshake = -1;
} catch (CancelledKeyException ckx) {
handshake = -1;
}
if (handshake == 0) {
SocketState state = SocketState.OPEN;
// Process the request from this socket
// 处理请求
if (event == null) {
state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
} else {
state = getHandler().process(socketWrapper, event);
}
if (state == SocketState.CLOSED) {
close(socket, key);
}
} else if (handshake == -1 ) {
close(socket, key);
} else if (handshake == SelectionKey.OP_READ){
socketWrapper.registerReadInterest();
} else if (handshake == SelectionKey.OP_WRITE){
socketWrapper.registerWriteInterest();
}
} catch (CancelledKeyException cx) {
socket.getPoller().cancelledKey(key);
} catch (Throwable t) {
socket.getPoller().cancelledKey(key);
} finally {
socketWrapper = null;
event = null;
//return to cache
if (running && !paused) {
processorCache.push(this);
}
}
}
}
Processor

Connector协议处理接口,负责将Endpoint接收到的Socket封装成Tomcat Request,并通过Adapter将其转为ServletRequest提交到Container容器处理,是对应用层的抽象。Processor是单线程的,Tomcat在同一次连接中复用Processor。相当于实现了HTTP协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)
throws IOException {

SocketState state = SocketState.CLOSED;
Iterator<DispatchType> dispatches = null;
do {
if (dispatches != null) {
DispatchType nextDispatch = dispatches.next();
state = dispatch(nextDispatch.getSocketStatus());
} else if (status == SocketEvent.DISCONNECT) {
// Do nothing here, just wait for it to get recycled
} else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) {
state = dispatch(status);
if (state == SocketState.OPEN) {
// There may be pipe-lined data to read. If the data isn't
// processed now, execution will exit this loop and call
// release() which will recycle the processor (and input
// buffer) deleting any pipe-lined data. To avoid this,
// process it now.
// 处理socket,封装成Tomcat的request、response,交由getAdapter().service(request, response);处理
state = service(socketWrapper);
}
} else if (status == SocketEvent.OPEN_WRITE) {
// Extra write event likely after async, ignore
state = SocketState.LONG;
} else if (status == SocketEvent.OPEN_READ){
state = service(socketWrapper);
} else {
// Default to closing the socket if the SocketEvent passed in
// is not consistent with the current state of the Processor
state = SocketState.CLOSED;
}

if (state != SocketState.CLOSED && isAsync()) {
state = asyncPostProcess();
}

if (dispatches == null || !dispatches.hasNext()) {
// Only returns non-null iterator if there are
// dispatches to process.
dispatches = getIteratorAndClearDispatches();
}
} while (state == SocketState.ASYNC_END ||
dispatches != null && state != SocketState.CLOSED);

return state;
}
Adapter

Adapter用于将封装好的Request交给Container进行具体的处理,将请求适配到Servlet容器进行具体的处理

1
getAdapter().service(request, response);

这里会调用Pipeline的第一个Value来进行处理

1
2
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response);

欢迎关注我的其它发布渠道