上篇文章实现的连接器是十分简陋的,这一章就比较复杂了。首先默认连接器这个名字,现在tomcat用的连接器是Coyote,默认连接器由于性能的问题已经弃用了,但是这个连接器仍然是个很好的学习工具。这一章和前一章最大的区别就是加入了多线程,我会用自己的理解试着阐述清楚这个场景。
首先来看一下UML图:
connector.initialize();
connector.start();
应用程序入口也是在BootStrap类
public void initialize()
throws LifecycleException {
...
this.initialized=true;
Exception eRethrow = null;
// Establish a server socket on the specified port
try {
serverSocket = open();
...
}
在启动connect线程之前,先初始化,调用initialize(),函数中再通过open方法创建serverSocket
// Acquire the server socket factory for this Connector
ServerSocketFactory factory = getFactory();
// If no address is specified, open a connection on all addresses
if (address == null) {
log(sm.getString("httpConnector.allAddresses"));
try {
return (factory.createSocket(port, acceptCount));
} catch (BindException be) {
throw new BindException(be.getMessage() + ":" + port);
}
}
// Open a server socket on the specified address
try {
InetAddress is = InetAddress.getByName(address);
log(sm.getString("httpConnector.anAddress", address));
try {
return (factory.createSocket(port, acceptCount, is));
...
}
在open方法中,通过ServerSocketFactory 来创建serverSocket,并且分为有无具体地址这两种情况。
public void start() throws LifecycleException {
...
// Start our background thread
threadStart();
// Create the specified minimum number of processors
while (curProcessors < minProcessors) {
if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
break;
HttpProcessor processor = newProcessor();
recycle(processor);
}
}
初始化完了之后,再调用start方法。threadstart方法启动连接器线程。下面那个while循环会创建数量为minProcessors的处理器实例,并将其压入名为processors的栈中。
private HttpProcessor newProcessor() {
HttpProcessor processor = new HttpProcessor(this, curProcessors++);
if (processor instanceof Lifecycle) {
try {
((Lifecycle) processor).start();
} catch (LifecycleException e) {
log("newProcessor", e);
return (null);
}
}
created.addElement(processor);
return (processor);
}
public void start() throws LifecycleException {
if (started)
throw new LifecycleException
(sm.getString("httpProcessor.alreadyStarted"));
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
threadStart();
}
并且我们还要注意到,每创建一个processor实例,都会开启一个处理器线程,这些线程都会到await里面的while循环里面等待assign方法的notifyAll。(后面会详细讲这里)
到了这一步,前面做的准备就都做好了,下面就进入到HttpConnector的run方法,等待Http请求的到来。
/**
* 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 (!stopped) {
// Accept the next incoming connection from the server socket
Socket socket = null;
try {
socket = serverSocket.accept();
socket.setTcpNoDelay(tcpNoDelay);
...
continue;
}
// Hand this socket off to an appropriate processor
HttpProcessor processor = createProcessor();
if (processor == null) {
try {
log(sm.getString("httpConnector.noProcessor"));
socket.close();
} catch (IOException e) {
;
}
continue;
}
//将socket交给processor
processor.assign(socket);
// The processor will recycle itself when it finishes
}
// Notify the threadStop() method that we have shut ourselves down
synchronized (threadSync) {
threadSync.notifyAll();
}
}
这里run方法,用serverSocket的accept方法拿到socket,然后通过assign方法,交给一个processor。
下面详细地来讲一下assign方法和await方法,因为这两个方法是多线程同时处理多个请求的关键。
当HttpConnector创建的HttpProcessor实例调用了assign方法之后,传入的socket会赋值给成员变量socket(available的初始值是false,所以第一次进来的时候while循环会被跳过),然后available会被置成true,再调用notifyAll,此时在await的while循环中被挂起的处理器县城就被唤醒了,此时available为true,于是跳出循环,将socket值拿到,把available置为false,再调用notifyAll(),为什么这里要这么做呢?因为如果await这边一个请求还没处理完,assign那边又来一个请求,如果此时available为true,岂不是要被困在while循环中了?为false才能在被notifyAll唤醒的时候脱离这个循环,让assgin尽快地把socket传给await方法。
前面有说到,在初始化的时候,会创建minProcessor数量的处理器线程,并且在await的while循环里待命,大多时候情况都像上面那样运转正常,下面我们来思考一下极端的场景,打个比方,await那里有5个线程在待命,然而处理5个Http请求的时间超过了assign发过来请求的时间,这也就意味着在最后一个处理器线程将available置为false之后,就去处理请求去了,这时await的while循环中已经没有线程在待命了,此时,又来了一个请求,available为false,while循环被跳过,调用notifyAll然后发现处理器线程那边没有响应,这个时候这么办呢?available为false,所以没办法调用wait挂起连接器线程,assign方法又不是一个循环,可以一直在等待。这里我有点想不通。以后想通了再做解答吧。
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);
}
...
}
await方法拿到socket之后就返回,到了run方法里面,之后再调用process方法,还是老三套,创建HttpRequest,HttpResponse,解析头部,获取cookies,获取参数等等,处理完这些就调用recycle方法,把processor压回栈里面去。
这里的process方法和之前的不同之处在于,这里添加了很多Http/1.0协议的相关解析,具体的就不展开讲了。