本文是Tomcat源码阅读系列的第四篇文章,本系列前三篇文章如下:
Tomcat源码阅读系列(一)使用IntelliJ IDEA运行Tomcat6源码
Tomcat源码阅读系列(二)Tomcat总体架构
Tomcat源码阅读系列(三)启动和关闭过程
本文主要介绍Tomcat的Connector连接器相关。
Tomcat主要由两大核心组件,一个是Coyote为代表的connector,一个是Catalina为代表的container。connector负责的是底层的网络通信的实现,而container负责的是上层servlet业务的实现。对于tomcat的servlet container这部分代码很少改动,一个应用服务器的性能很大程度上取决于网络通信模块connector的性能,因此connector对于tomcat而言是重中之重。本文首先从应用层次分析Tomcat所有的connector种类及用法,然后介绍Connector的初始化以及使用过程,最后对一些关节步骤进行说明。本文以BIO的Http11Protocol为例进行说明,如有需要可自行阅读Http11AprProtocol、AjpAprProtocol、AjpProtocol、Http11NioProtocol和JkCoyoteHandler等,不同的协议实现对接不同的技术,如NIO、APR和AJP等,具体技术说明可以参考《Tomcat源码阅读系列(二)Tomcat总体架构》。
根据Connector与外界交互协议的不同,可以将Connector分为使用HTTP协议进行交互类型和使用AJP协议交互类型两种,而这两种又可以分别于APR组件机型配合,所以产生的Protocol比较多,功能相对复杂。
BIO HTTP/1.1 Connector配置
使用ThreadPoolExecutor的BIO HTTP/1.1 Connector配置
SSL配置
NIO HTTP/1.1 Connector配置
AJP 1.3 Connector配置
Native APR Connector配置
具体如何安装,可自行搜索。配置如下:
查看Tomcat当前使用的是哪个protocol可以根据Tomcat的启动日志来查看
对Tomcat7而言,
bio
信息: Starting ProtocolHandler ["http-bio-8080"] 2015-8-20 22:17:50 org.apache.coyote.AbstractProtocol start
nio
信息: Starting ProtocolHandler ["http-nio-8080"] 2015-8-20 22:27:50 org.apache.coyote.AbstractProtocol start
apr
信息: Starting ProtocolHandler ["http-apr-8080"] 2015-8-20 22:29:50 org.apache.coyote.AbstractProtocol start
对Tomcat6而言,
bio
2015-8-20 22:25:36 org.apache.coyote.http11.Http11Protocol start
信息: Starting Coyote HTTP/1.1 on http-8080
nio
2015-8-20 22:30:38 org.apache.coyote.http11.Http11NioProtocol start
信息: Starting Coyote HTTP/1.1 on http-8080
注意:Tomcat6的处理线程的名字如http-8080-1,而Tomcat7如http-bio-8080-exec-1,增加了protocol的协议信息。
/**
* Server socket acceptor thread.
*/
protected class Acceptor implements Runnable {
/**
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
*/
public void run() {
// Loop until we receive a shutdown command
while (running) {
// Loop if endpoint is paused
while (paused) { //如果endpoint暂停的话,这里也停止运行。
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Ignore
}
}
// Accept the next incoming connection from the server socket
try { //acceptSocket会阻塞,直到新的请求的到来。
Socket socket = serverSocketFactory.acceptSocket(serverSocket);
serverSocketFactory.initSocket(socket);
// Hand this socket off to an appropriate processor
if (!processSocket(socket)) { //关键代码 开启新线程或者利用老线程处理。
// Close socket right away
try {
socket.close();
} catch (IOException e) {
// Ignore
}
}
}catch ( IOException x ) {
if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);
} catch (Throwable t) {
log.error(sm.getString("endpoint.accept.fail"), t);
}
// The processor will recycle itself when it finishes
}
}
}
/**
* Process given socket.
*/
protected boolean processSocket(Socket socket) {
try {
if (executor == null) {//关键代码,决定是使用Tomcat自己的线程管理系统还是JDK的ThreadPoolExecutor,
getWorkerThread().assign(socket);
} else {
executor.execute(new SocketProcessor(socket));//如果Server.xml中的Connector配置了executor="tomcatThreadPool"则,使用JDK的ThreadPoolExecutor
}
} catch (Throwable t) {
// This means we got an OOM or similar creating a thread, or that the pool and its queue are full
log.error(sm.getString("endpoint.process.fail"), t);
return false;
}
return true;
}
以上代码比较关键,对于Tomcat6而言,默认情况下,是使用自己的模块管理线程的,只有配置了executor="tomcatThreadPool"才使用JDK的ThreadPoolExecutor,JDK的ThreadPoolExecutor管理线程时,具有线程收缩管理的功能,但是使用Tomcat 6自己的线程管理模块并没有伸缩管理的功能。
protected Worker getWorkerThread() {
// Allocate a new worker thread
synchronized (workers) {
Worker workerThread;
while ((workerThread = createWorkerThread()) == null) {//如果得不到workThread,则等待。只有在线程数到达最大线程数时,返回值才为null,这个时候需要等待有线程处理完某项工作,执行recycleWorkerThread中的workers.notify();才能被唤醒。
try {
workers.wait();
} catch (InterruptedException e) {
// Ignore
}
}
return workerThread;
}
}
protected Worker createWorkerThread() {
synchronized (workers) {// 必须为线程安全的操作,因为可能有多个线程同时过来,这时候对WorkerStack的操作的非线程安全的,需要使用synchronized保证线程安全
if (workers.size() > 0) {//如果WorkerStack不为空,则从WorkerStack中取值
curThreadsBusy++;//
return workers.pop();
}
if ((maxThreads > 0) && (curThreads < maxThreads)) {//如果最大线程数大于0并且当前最大线程数小于规定最大线程数
curThreadsBusy++;
if (curThreadsBusy == maxThreads) {
log.info(sm.getString("endpoint.info.maxThreads",
Integer.toString(maxThreads), address,
Integer.toString(port)));
}
return (newWorkerThread());//返回一个新线程
} else {
if (maxThreads < 0) {//最大线程数为负数,则无限制的返回Worker
curThreadsBusy++;
return (newWorkerThread());
} else {
return (null);//如果大于最大线程数,则返回null
}
}
}
}
protected Worker newWorkerThread() {
Worker workerThread = new Worker();
workerThread.start();
return (workerThread);
}
public void start() {
thread = new Thread(this);//新建线程
thread.setName(getName() + "-" + (++curThreads));//此处设置线程名,http-端口
thread.setDaemon(true);//所有工作线程都为daemon的
thread.start();
}
protected void recycleWorkerThread(Worker workerThread) {//处理完成后,将线程放到WorkerStack中!回收操作
synchronized (workers) {//必须为线程安全的,因为WorkerStack为非线程安全
workers.push(workerThread);//放到WorkerStack中
curThreadsBusy--;
workers.notify();
}
}
public void run() {
// Process requests until we receive a shutdown signal
while (running) {
// Wait for the next socket to be assigned
Socket socket = await();//等待新的请求到来后调用notifyall()方法
if (socket == null)
continue;
// Process the request from this socket
if (!setSocketOptions(socket) || !handler.process(socket)) {//处理socket
// Close socket
try {
socket.close();
} catch (IOException e) {
}
}
// Finish up this request
socket = null;
recycleWorkerThread(this);//回收worker
}
}
以上代码分析,可以知道,只有对WorkerStack的存取操作,并没有线程数的收缩操作,
在峰值过后,线程数仍然会维持在峰值位置!
public boolean process(Socket socket) {
Http11Processor processor = recycledProcessors.poll();//每次从资源池中取
try {
if (processor == null) {
processor = createProcessor();//取不到则创建
}
//....省略
processor.process(socket);//Http11Processor.process
return false;
}//...省略
catch (Throwable e) {//此处使用了Throwable顶级异常
Http11Protocol.log.error
(sm.getString("http11protocol.proto.error"), e);
} finally {
if (processor instanceof ActionHook) {
((ActionHook) processor).action(ActionCode.ACTION_STOP, null);
}
//回收processor
recycledProcessors.offer(processor);
}
return false;
}
Http11Processor.process方法中的核心代码就是
adapter.service(request, response);
此处的adapter是CoyoteAdapter,次类的地位非常重要,是连接catalina和coyote组件的适配器。是适配器模式的一个典型应用,关于适配器模式可以查看 设计模式笔记9:适配器模式(Adapter Pattern)。
CoyoteAdapter实现了Adapter,同时使用Connector作为自己的内部对象,是一种对象适配器模式,遵循了多用组合少用继承的设计准则,其适配coyote组件,为catalina组件提供服务。
而Adapter.service()方法的核心代码就是
connector.getContainer().getPipeline().getFirst().invoke(request, response);
这段代码,直接将请求交给了catalina组件进行处理。关于catalina组件的处理过程,将在下篇博文中进行分析。