How Tomcat Works 读书笔记(第四章)

本文详细介绍了Tomcat连接器的工作原理及其内部实现机制,包括连接器的职责、HTTP1.1新特性、Connector接口及其实现类HttpConnector的运作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[size=medium]
Tomcat的连接器是一个单独的模块,它可以插入到一个servlet容器里面,现在一些比较常见的连接器有Coyote,mod_jk, mod_jk2,和mod_webapp.一个Tomcat的连接器必须要满足下面的要求:
1. 它必须实现org.apache.catalina.Connector接口
2. 它必须要创建一个实现org.apache.catalina.Request对象的request
3. 它必须要创建一个实现org.apache.catalina.Response对象的response
Tomcat 4的连接器和第三章的差不多,它也是等待HTTP请求,创建reqeust和response对象,而后把request和response对象传给container容器。它是调用container的invoker方法来实现传递的:
[/size]

Public void invoke (org.apache.catalina.Reqeust request, org.apache.catalina.Response response);

[size=medium]
在invoke方法里面,container加载servlet类,调用它的service方法,管理session,并使用log等组件。这个connector对第三章的connector做了一些优化,第一点就是提供了一个池,避免了对象创建的开销,第二点,在很多地方用char数组代替了string。
HTTP 1.1 新特性:
1. 持久连接:这个我们比较熟悉,就是复用Connection,来下载多个资源;
2. 块编码: 在content-length没有准备好的情况下,每块数据都会提供一个length, 用来指示块长度;
3. 100状态码的使用, 这个一般用作client来确定服务器状态用的,这个一般用作客户端准备传送一个大的request body,但无法确定服务器是否接受它,如果服务器此刻是down的,那么客户端传送一个大的request body是一个资源浪费。
Connector接口:
1. Tomcat的连接器都是实现org.apache.catalina.Connector接口,它的主要的方法是getContainer, setContainer, createRequest,createResponse. setContainer会使一个Connector和一个Container联系起来,其他的createRequest和createResponse比较容易理解,就是创建一个Request和Response对象。
2. Org.apache.catalina.http.HttpConnector是它的实现类。下面我们看一下类图:
[/size]
[img]http://dl.iteye.com/upload/attachment/179637/536c7bed-4115-39d1-a807-48b6ece9b34e.jpg[/img]
[size=medium]
我们来看HttpConnector类 的实现吧:
首先在它的open方法里面调用ServerSocketFactory创建一个ServerSocket,DefaultServerSocketFactory实现ServerSocketFactory,这个很好理解,就是创建一个ServerSocket。
在第三章里面,我们的HttpConnector只有一个HttpProcessor实例,它一次只能请求一个Http请求,在Tomcat连接器里面,它有一个HttpProcessor对象池,每个HttpProcessor都在独自的线程里面运行,所以HttpProcessor可以同时处理多个Http请求。
HttpConnector维护了一个HttpProcessor池来避免HttpProcessor的创建, HttpProcessor实例是维护在一个栈中的,
Private Stack processors = new Stack();
在HttpConnector中有个minProcessors和maxProcessors两个变量,HttpConnector会创建minProcessors个HttpProcessor,它会创建HtttpProcessor,直到maxProcessors达到后。 变量curProcessor保存着HttpProcessor创建的数量。
在HttpConnector的start方法里面会调用下面的代码来创建初始的Processor。
[/size]

While(curProcessors < minProcessors) {
If( maxProcessors > 0 && curProcessor >= maxProcessors){
Break;
}
HttpProcessor processor = newProcessor();
Recycle(processor);
}

[size=medium]
Recycle方法是将HttpProcessor压入到栈中去的。
HttpProcessor是负责解析HTTP Request请求头和Header的,一个HttpProcessor和一个request和一个response对象关联。 在HttpProcessor的构造函数里面会调用createRequest和createResponse方法来创建Request对象和Response对象。
当有一个请求来临时,HttpProcessor会从对象池中取得一个HttpProcessor对象,如果到达maxProcessors的时候,HttpConnector直接关闭socket,Http request将不会被处理。获得一个HttpProcessor方法后,将会调用HttpProcessor的assign方法:
Processor.assing(socket);
HttpProcessor对象是利用SocketInputStream读取请求头,解析Http Request。我们现在就来看一下assign方法吧。
[/size]

/**
* Process an incoming TCP/IP connection on the specified socket. Any
* exception that occurs during processing must be logged and swallowed.
* <b>NOTE</b>: This method is called from our Connector's thread. We
* must assign it to our own thread so that multiple simultaneous
* requests can be handled.
*
* @param socket TCP socket to process
*/
synchronized void assign(Socket socket) {

// Wait for the Processor to get the previous Socket
// 在创建的时候,available是false的,所以这个while循环将被跳过
while (available) {
try {
wait();
} catch (InterruptedException e) {
}
}

// Store the newly available Socket and notify our thread
// 我们将available设置成true,调用notifyAll来唤醒等待当前对象的锁
this.socket = socket;
available = true;
notifyAll();

if ((debug >= 1) && (socket != null))
log(" An incoming request is being assigned");
// 这个方法就可以返回了,HttpConnector就可以相应下一个请求了。

}


[size=medium]
对了,我们要完全理解这个HttpProcessor线程,我们应该先看一下HttpConnector的newProcessor方法。
[/size]

/**
* Create and return a new processor suitable for processing HTTP
* requests and returning the corresponding responses.
*/
private HttpProcessor newProcessor() {

// if (debug >= 2)
// log("newProcessor: Creating new processor");
// 首先会创建一个HttpProcessor对象
HttpProcessor processor = new HttpProcessor(this, curProcessors++);
if (processor instanceof Lifecycle) {
try {
// 在这里调用了processor的start方法
((Lifecycle) processor).start();
} catch (LifecycleException e) {
log("newProcessor", e);
return (null);
}
}
created.addElement(processor);
return (processor);
}

[size=medium]
其实HttpProcessor的构造函数很简单,主要会调用HttpConnector的createRequest和createResponse方法。
我们看到在创建HttpProcessor的时候会调用start方法,在start方法里面调用threadStart方法,在start线程里面将该线程设置成守护线程,然后调用start方法,实际上会执行现场的run方法,我们就重点看一下run方法吧。其实run方法很简单,就是得到一个socket,然后处理该socket,处理完毕之后调用HttpConnector的recycle方法,将该对象压入processors栈中。
[/size]

/**
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
*/
public void run() {

// Process requests until we receive a shutdown signal
while (!stopped) {

// Wait for the next socket to be assigned
Socket socket = await();
if (socket == null)
continue;

// Process the request from this socket
try {
process(socket);
} catch (Throwable t) {
log("process.invoke", t);
}

// Finish up this request
connector.recycle(this);

}

// Tell threadStop() we have shut ourselves down successfully
synchronized (threadSync) {
threadSync.notifyAll();
}

}

[size=medium]
下面最重要的就是await方法了,这个方法写的很巧妙。我们来看一下吧:
[/size]

/**
* Await a newly assigned Socket from our Connector, or <code>null</code>
* if we are supposed to shut down.
*/
private synchronized Socket await() {

// 在初始化的时候availabe是false,所以会调用wait方法
// Wait for the Connector to provide a new Socket
while (!available) {
try {
wait();
} catch (InterruptedException e) {
}
}

// 其实我们刚才也看到了,调用了assign方法后,available的值会变成true,
// 然后将socket返回,我们可以看到在这里将socket赋给了一个局部变量
// 这一点非常高明,使得HttpProcessor可以复用了。
// Notify the Connector that we have received this Socket
Socket socket = this.socket;
available = false;
notifyAll();

if ((debug >= 1) && (socket != null))
log(" The incoming request has been awaited");

return (socket);

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值