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 所支
持的两种协议:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
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 所代表
的类创建了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 = new Poller();
Thread pollerThread = new Thread(pollers, 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()方法中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值