Tomcat 工作流程(二)

Connector 是什么?

tomcat 中,connector 是用于负责接收来自客户端的连接,其主要任务是负责处理浏览器发送过来的请求,并交由后续的代码进行处理。connector 对象持有 ProtocolHandler 对象;ProtocolHandler 对象持有 AbstractEndpoint 对象。AbstractEndpoint 负责创建服务器套接字,并绑定到监听端口;同时还创建accepter 线程来接收客户端的连接以及 poller 线程来处理连接中的读写请求。

1,实例化 Connector,构造一个 Connector 对象

2,调用 Connector initIntenal方法,初始化 Connetor

3,调用 ProtocolHanlder init 方法,完成ProtocolHanlder 的初始化。这个过程包括了创建线程池并创建一个线程处理浏

览器请求

4,调用 Connector startIntenal 方法,启动 Connector

5,调用ProtocolHandler 的 start 方法,启动 Protocolhanlder

Connector 的配置

既然是处理浏览器请求,那么需要支持 http 协议,在 Tomcat 中有两种协议处理器:HTTP/1.1 AJP/1.3 协议处理器。在 server.xml 中已经指明 tomcat 所支持的两种协议:

port="8080" protocol="HTTP/1.1"

connectionTimeout="20000"

redirectPort="8443" />

port="8009" protocol="AJP/1.3" redirectPort="8443" />

Connector 的内部初始化

Connector 的创建过程主要是初始化 ProtocolHandlerserver.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 所代表的类创建了ProtocolHandler 并赋值给了protocolHandler属性。setProtocol 方法代码如下:

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)) {

// tomcat8 默认配置

setProtocolHandlerClassName

("org.apache.coyote.http11.Http11NioProtocol");

} else if ("AJP/1.3".equals(protocol)) {

setProtocolHandlerClassName

("org.apache.coyote.ajp.AjpNioProtocol");

} else if (protocol != null) {

setProtocolHandlerClassName(protocol);

}

}

}

在创建 Http11NioProtocol 实例的时候,会创建 NioEndpoint、Http11ConnectionHandler 实例。

public Http11NioProtocol() {

// 创建 nioEndPoint

endpoint=new NioEndpoint();

// 创建 Http11ConnectionHandler

cHandler = new Http11ConnectionHandler(this);

((NioEndpoint) endpoint).setHandler(cHandler);

// 是指 socket 被关闭时逗留的时间,值为-1

// 在这段时间内,socket 会尽量把未送出去的数据给发出去。

setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);

// 设置读取数据超时

setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);

// 设置 tcp_nodelay,

setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);

}

NioEndpoint Connector 中处理客户端连接的核心类,负责创建服务器套接字,并绑定到监听端口;同时还创建 accepter 线程来接收客户端的连接以及 poller线程来处理连接中的读写请求。

当 Connector 调用 init()方法时本质是调用 initInternal(),initInternal()方法又会最终调用 endpoint init()方法

NioEndpoint 的 bind 方法

public void bind() throws Exception {

// 打开 serverSocketChannel

serverSock = ServerSocketChannel.open();

// 设置 socket 属性

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());

//.....省略

}

Connector 调用 start()方法时会执行 startInternal()方法,startInternal()方法则调用 endpoint start()方法。 endpoint start()方法是在 AbstractEndPoint 中实现的,并调用推迟到 NioEndPoint 中的 startInternal()方法。

@Override

public void startInternal() throws Exception {

if (!running) {

running = true;

paused = false;

// 创建缓存容器

processorCache = new

SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,

socketProperties.getProcessorCache());

eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,

socketProperties.getEventCache());

nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,

socketProperties.getBufferPool());

// 创建线程池

if ( getExecutor() == null ) {

createExecutor();

}

// 初始化计数器 Latch

initializeConnectionLatch();

// 创建 Poller 线程

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();

}

// 创建 Acceptor 线程

startAcceptorThreads();

}

}

最重要的是创建 Poller Acceptor 线程。Acceptor 线程处理serverSocketChannel 的请求接收事件;Poller 处理 serverSocketChannel 的读写事件。

protected class Acceptor extends AbstractEndpoint.Acceptor {

@Override

public void run() {

int errorDelay = 0;

//......

try {

//通过同步计数器来限制连接数目

//当连接数目超过上限时,则等待

//其中同步计算器是通过继承 AQS 实现的

//默认的最大连接数是 10000

countUpOrAwaitConnection();

SocketChannel socket = null;

try {

//接收连接,此处并不是使用 selector 实现

//在前面的代码中已知 serverSock 是阻塞模式的。

socket = serverSock.accept();

} catch (IOException ioe) {

//we didn't get a socket

countDownConnection();

// Introduce delay if necessary

errorDelay = handleExceptionWithDelay(errorDelay);

// re-throw

throw ioe;

}

// Successful accept, reset the error delay

errorDelay = 0;

// setSocketOptions() will add channel to the poller

// if successful

if (running && !paused) {

// setSocketOptions 中将接收到的 socket 传给 poller 线程进行处理

if (!setSocketOptions(socket)) {

countDownConnection();

closeSocket(socket);

}

} else {

countDownConnection();

closeSocket(socket);

//.....

}

Acceptor 线程会反复执行 serverSock.accept()等待客户端连接的到来,等接收到一个客户端连接时,会把接收到的 socket 传给后续的 poller 线程处理,其执行过程在 setSocketOptions()方法中。

你可能感兴趣的:(Tomcat 工作流程(二))