先上个图先,一个只有我自己能看懂的url时序图.
这个基本上是connentor初始化的时候,初始化了Http11Protocol,接着初始化JIoEndpoint,初始化介绍后,connentor调用start()方法开始工作鸟,接着调用Http11Protocol,JIoEndpoint的start()方法,JIoEndpoint的start()方法大有可为,看代码:
public void start()
throws Exception {
// Initialize socket if not done before
if (!initialized) {
init();
}
if (!running) {
running = true;
paused = false;
// Create worker collection
if (executor == null) {
workers = new WorkerStack(maxThreads);
}
// Start acceptor threads
for (int i = 0; i < acceptorThreadCount; i++) {
Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
acceptorThread.setPriority(threadPriority);
acceptorThread.setDaemon(daemon);
acceptorThread.start();
}
}
}
start()方法中如果没有外部的executor的话,会使用的自己内部的简单的工作线程池WorkerStack,这个线程池放着处理请求的线程集合,当请求的数量超过定义的最大支持线程数,那么后面的请求一直阻塞等待只到有可以使用的线程为止。
看代码
for (int i = 0; i < acceptorThreadCount; i++) {
Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
acceptorThread.setPriority(threadPriority);
acceptorThread.setDaemon(daemon);
acceptorThread.start();
}
acceptorThreadCount一般的值就是1,这里另外开启一个线程,Acceptor是个内部类, 看代码:
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) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Ignore
}
}
// Accept the next incoming connection from the server //socket
try {
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
}
}
}
Acceptor实现runnable接口,在run方法里面是一个while循环,当JIoEndpoint的start()的已经执行过的话,那么running为true,while循环就一直运行下去,只到JIoEndpoint执行了resume,stop等方法。while循环里面利用工厂类来产生一个socket,这里只是把socket编程的步骤移动到了工厂里面了,符合类单一责任原则。
最主要的是调用JIoEndpoint的processSocket方法,另外如果方法调用返回false的话,socket会被关闭,因为false一般代表了这次请求处理可能有问题,另外在这个类中,发现如果出现异常的话,基本上是不用throw异常的,一是可能考虑到这只是对一个请求而已,抛出异常没有意义,二是为了性能的考虑,所有一般的方法都是以boolean来判断异常和正常与否。我们看下JIoEndpoint的processSocket方法:
protected boolean processSocket(Socket socket) {
try {
if (executor == null) {
getWorkerThread().assign(socket);
} else {
executor.execute(new SocketProcessor(socket));
}
} 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;
}
有外部executor的话,那么外部的executor来执行这个socket;没有的话,就需要JIoEndpoint的内部线程类Worker来完成了
先看如果有executor的情况,此时调用很简单,一个runnable实现SocketProcessor被调用
public void run() {
// Process the request from this socket
if (!setSocketOptions(socket) || !handler.process(socket)) {
// Close socket
try {
socket.close();
} catch (IOException e) {
}
}
// Finish up this request
socket = null;
}
设置socket的属性,然后调用内部接口Handler的process处理socket的具体内容,Handler的具体实现在Http11Protocol内部类Http11ConnectionHandler中,这个后面再讲。
这样子JIoEndpoint的工作基本完成了。
再来看当executor==null的时候的分支,首先需要获得一个工作线程,但是如果线程池设置了上限的话,可能会阻塞只到有可用的线程或者可以创建新的线程,之后我们来看Worker的assign方法,看这个方法需要和Worker的await方法一起来看:
protected boolean available = false;
synchronized void assign(Socket socket) {
// Wait for the Processor to get the previous Socket
while (available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Store the newly available Socket and notify our thread
this.socket = socket;
available = true;
notifyAll();
}
private synchronized Socket await() {
// Wait for the Connector to provide a new Socket
while (!available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Notify the Connector that we have received this Socket
Socket socket = this.socket;
available = false;
notifyAll();
return (socket);
}
首先调用assign时,是不用wait的,但是await方法就会阻塞在这里,这里这样做的第1个原因出现:
assign()方法设置了socket后才设置available,这里主要是为了await()方法返回的socket是正确的,不为空的,是这个线程的,当assign()最后nofifyAll(),那么await()方法可以自由的执行了
反过来看,当available为true的时候,await()可以执行,而assign()方法阻塞了,这里有第2个原因出现,Worker存活在线程池中,在这两个方法中我们用的同一个属性socket,所以这个每个请求来了之后都会改变这个属性,所以这两个方法必须同步,在await()返回当前的socket之前,是不能设置这个socket的.
我们在来看Worker的run()方法
public void run() {
// Process requests until we receive a shutdown signal
while (running) {
// Wait for the next socket to be assigned
Socket socket = await();
if (socket == null)
continue;
// Process the request from this socket
if (!setSocketOptions(socket) || !handler.process(socket)) {
// Close socket
try {
socket.close();
} catch (IOException e) {
}
}
// Finish up this request
socket = null;
recycleWorkerThread(this);
}
}
这个里面调用了await()方法,就是上面所说的获得正确的socket,然后执行和SocketProcessor内部类实现的一样的功能的代码,最后做一些线程回收的工作
这里有一点可能会影响到性能,就是Worker线程池最大时,就需要阻塞等待,知道有可以用的线程,这个可能是考虑到如果线程开太多,内部压力大,线程的切换消耗大,这个可以作为tomcat调优的一个点,另外一个就是可以使用外部的线程池,这个也可以尝试
哇,真长!