好了,接着上一节的内容。我们已经很清楚serverSocket = open();这个方法的作用了。那么我们继续说第二个需要注意的地方。
第二个需要注意的地方:HttpProcessor processor = createProcessor();这个方法。我们先来解决以下几个小问题:
一,为什么HttpConnector为什么要有HttpProcessor实例?
HttpProcessor是一个处理HTTP请求的一个实例,当有用户连接上来后,那么就需要把这个socket对象传给HttpProcessor对象,好让这个对象去处理HTTP请求;
二,HttpConnection与HttpProcessor的关系?
HttpConnection与HttpProcessor是关联的关系,而且是一对多的关系,HttpConnector实例有一个HttpProcessor对象池,说白了就是一个存放HttpProcessor的容器,每个HttpProcessor实例都运行在其自己的线程中,这样HttpConnection实例就可以同时处理多个HTTP请求。如果不这样做的话,那么就意味着每当有一个用户连接上来的话,就必须去实例化一个HttpProcessor对象,那么这样是十分的占用资源的。
三,HttpConnection是如何维护HttpProcessor实例的?
HttpProcessor实例存储在一个名为processors的java.io.Stack类型变量中:
private Stack processors = new Stack();
在HttpConnector中,创建的HttpProcessor实例的个数由两个变量决定,minProcessors和maxProcessors。默认情况下,minProcessor实例的个数由两个变量决定:minProcessor和maxProcessor。默认情况下,minProcessors的值为5,maxProcessors的值为20,可以通过setMinProcessors()方法和setMaxProcessors()方法对这两个数值进行修改:
protected int minProcessors = 5;
private int maxProcessors = 20;
初始,HttpConnector对象会依据minProcessors的数值来创建HttpProcessor实例。若是请求的数目超过了HttpProcessor实例所能处理的范围,HttpConnector实例就会创建更多的HttpProcessor实例,直到HttpProcessor实例的数目达到maxProcessors限定的范围。若是HttpProcessor实例的数目已经达到了maxProcessors限定的数值,但还是不够用,此时引入的HTTP请求就会被忽略掉。若希望HttpConnector可以持续地创建HttpProcessor对象,就可以将变量maxProcessors的值设置为负数。此外,变量curProcessors保存了当前已有的HttpProcessor实例的数量。那么我们先来看看是如何创建初始化数量的。
org.apache.catalina.connector.http.HttpConnector中的run方法部分实现:
/**
* The current number of processors that have been created.
*/
private int curProcessors = 0;
/**
* The minimum number of processors to start at initialization time.
*/
protected int minProcessors = 5;
/**
* The maximum number of processors allowed, or <0 for unlimited.
*/
private int maxProcessors = 20;
// Create the specified minimum number of processors
while (curProcessors < minProcessors) {//如果当前的HttpProcessor数量curProcessors小于minProcessors,那么进入循环
if ((maxProcessors > 0) && (curProcessors >= maxProcessors))//如果最大数量的maxProcessors大于0并且当前的curProcessors大或等于maxProcessors,那么,那么退出,否则,创建新的HttpProcessor实例
break;
HttpProcessor processor = newProcessor();//负责创建HttpProcessor实例,并将curProcessors加1
recycle(processor);//将新创建的HttpProcessor对象入栈,这个到说到生命周期再说
}
看看newProcessor()方法的实现:
org.apache.catalina.connector.http.HttpConnector
/**
* The set of processors that have ever been created.
*/
private Vector created = new Vector();
/**
* 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 processor = new HttpProcessor(this, curProcessors++);
if (processor instanceof Lifecycle) {
try {
((Lifecycle) processor).start();//调用processor的start()方法,注意每一个HttpProcessor都运行在它们自己的线程里面
} catch (LifecycleException e) {
log("newProcessor", e);
return (null);
}
}
created.addElement(processor);//把processor放入vector容器中
return (processor);
}
大多数时间里,createProcessor()方法并不会创建一个新的HttpProcessor实例,相反,它会从池中获得一个对象。如果栈中还有一个HttpProcessor实例可以使用,createProcessor()方法就从栈中获取一个对象,将其返回,如果栈中已经空了,已经创建的HttpProcessor实例的数量还没有超过限定的最大值,createProcessor()方法就会创建一个新的HttpProcessor实例。但是,若HttpProcessor实例的数量已经达到了限定的最大值,createProcessor()方法返回null.此时,服务器会简单的关闭套接字。如果createProcessor()方法的返回值不为null,则会将客户端套接字传入到HttpProcessor类的assgin()方法中。还有,特别注意到HttpConnection与HttpProcesstor的关系。
那么第四点注意的可想而知就是processor.assgin(socket)这个方法了。
org.apache.catalina.connector.http.HttpProcessor
/**
* The socket we are currently processing a request for. This object
* is used for inter-thread communication only.
*/
private Socket socket = null;
/**
* Is there a new socket available?
*/
private boolean available = false;
// -------------------------------------------------------- Package Methods
/**
* 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
while (available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Store the newly available Socket and notify our thread
this.socket = socket;
available = true;
notifyAll();
if ((debug >= 1) && (socket != null))
log(" An incoming request is being assigned");
}
这个方法就是做了一个很简单的是,就是把把传入来的socket对象赋值给HttpProcessor的成员变量socket。现在,HttpProcessor实例的任务就是读取套接字的输入流,解析HTTP请求。这里需要注意的是,assign()方法直接返回,而不要等待HttpProcessor实例完成解析,这样HttpConnector才能接续服务传入的HTTP请求。也就是说assign()方法是运行在HttpConnection实例的一个线程中,而解析HTTP请求是运行在HttpProcessor实例中的线程中。这种做法是十分有好处的。把接收客户和处理解析给分开了。提高了效率。那么我们来看看HttpProcessor实例中的run()方法
org.apache.catalina.connector.http.HttpProcessor
// ---------------------------------------------- Background Thread Methods
/**
* 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);//将HttpProcess实例压回栈中
}
// Tell threadStop() we have shut ourselves down successfully
synchronized (threadSync) {
threadSync.notifyAll();
}
}
这个run()方法是在HttpConnection对象创建HttpProcessor实例后就调用其start()方法的,看看这个线程做了什么事:获取套接字,进行处理,调用连接器的recycle()方法将当前的HttpProcessor实例压回栈中。由此看出assgin()方法是获取HttpConnection实例传过来的socket实例,而await()方法是一旦assgin()方法获取到了socket实例后,那么await()能够马上得到传过来的socket实例,并且把给保存在自己的线程中,assgin()和await()这两个方法是运行在不同的两个线程中,那么问题来了,Tomcat是怎么处理它们之间的逻辑关系的??
这里得意于两个方法和一个boolean变量:java.lang.Object类的wait()方法和notifyAll()方法和available变量。
wait()方法会使当前线程进入等待状态,直到其他线程调用了这个对象的notify()方法和notifyAll()方法。那么来分析以下assgin()方法和await()方法的具体实现。
org.apache.catalina.connector.http.HttpProcessor
// -------------------------------------------------------- Package Methods
/**
* Is there a new socket available?
*/
private boolean available = false;
/**
* 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
while (available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Store the newly available Socket and notify our thread
this.socket = socket;
available = true;
notifyAll();
if ((debug >= 1) && (socket != null))
log(" An incoming request is being assigned");
}
// -------------------------------------------------------- Private Methods
/**
* Await a newly assigned Socket from our Connector, or <code>null</code>
* if we are supposed to shut down.
*/
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();
if ((debug >= 1) && (socket != null))
log(" The incoming request has been awaited");
return (socket);
}
当“处理器线程”刚刚启动时,available变量的值为false,假如现在有用户连接上来,那么assgin()方法中由于available值为false,所以它会把socket赋值给this.socket;而对于await()方法来说,在这一段时间内,由于!available为真,那么await()方法中的while()方法会一直阻塞,如果这时assgin()方法中把available的值修改为true时,并且调用notifyAll()方法,那么将继续执行await()方法,这时available这时为true,所以退出循环体,那么这时就执行socket = this.socket,await()方法拿到了socket实例了,接着又把available修改成false。
这里又有两个小问题了:
1,为什么await()方法要使用一个局部变量socket呢?而不是直接将成员变量socket返回呢?
因为使用局部变量可以在当前Socket对象处理完之前,继续接收下一个Socket对象。
2,为什么await()方法需要调用notifyAll()方法?
是为了防止出现另一个Socket对象已经到达,而此时变量available的值还是true的情况。在这种情况下,“连接器”会在assgin()方法内循环中阻塞,直到“处理器”调用了notifyAll()方法。
第二个需要注意的地方:HttpProcessor processor = createProcessor();这个方法。我们先来解决以下几个小问题:
一,为什么HttpConnector为什么要有HttpProcessor实例?
HttpProcessor是一个处理HTTP请求的一个实例,当有用户连接上来后,那么就需要把这个socket对象传给HttpProcessor对象,好让这个对象去处理HTTP请求;
二,HttpConnection与HttpProcessor的关系?
HttpConnection与HttpProcessor是关联的关系,而且是一对多的关系,HttpConnector实例有一个HttpProcessor对象池,说白了就是一个存放HttpProcessor的容器,每个HttpProcessor实例都运行在其自己的线程中,这样HttpConnection实例就可以同时处理多个HTTP请求。如果不这样做的话,那么就意味着每当有一个用户连接上来的话,就必须去实例化一个HttpProcessor对象,那么这样是十分的占用资源的。
三,HttpConnection是如何维护HttpProcessor实例的?
HttpProcessor实例存储在一个名为processors的java.io.Stack类型变量中:
private Stack processors = new Stack();
在HttpConnector中,创建的HttpProcessor实例的个数由两个变量决定,minProcessors和maxProcessors。默认情况下,minProcessor实例的个数由两个变量决定:minProcessor和maxProcessor。默认情况下,minProcessors的值为5,maxProcessors的值为20,可以通过setMinProcessors()方法和setMaxProcessors()方法对这两个数值进行修改:
protected int minProcessors = 5;
private int maxProcessors = 20;
初始,HttpConnector对象会依据minProcessors的数值来创建HttpProcessor实例。若是请求的数目超过了HttpProcessor实例所能处理的范围,HttpConnector实例就会创建更多的HttpProcessor实例,直到HttpProcessor实例的数目达到maxProcessors限定的范围。若是HttpProcessor实例的数目已经达到了maxProcessors限定的数值,但还是不够用,此时引入的HTTP请求就会被忽略掉。若希望HttpConnector可以持续地创建HttpProcessor对象,就可以将变量maxProcessors的值设置为负数。此外,变量curProcessors保存了当前已有的HttpProcessor实例的数量。那么我们先来看看是如何创建初始化数量的。
org.apache.catalina.connector.http.HttpConnector中的run方法部分实现:
/**
* The current number of processors that have been created.
*/
private int curProcessors = 0;
/**
* The minimum number of processors to start at initialization time.
*/
protected int minProcessors = 5;
/**
* The maximum number of processors allowed, or <0 for unlimited.
*/
private int maxProcessors = 20;
// Create the specified minimum number of processors
while (curProcessors < minProcessors) {//如果当前的HttpProcessor数量curProcessors小于minProcessors,那么进入循环
if ((maxProcessors > 0) && (curProcessors >= maxProcessors))//如果最大数量的maxProcessors大于0并且当前的curProcessors大或等于maxProcessors,那么,那么退出,否则,创建新的HttpProcessor实例
break;
HttpProcessor processor = newProcessor();//负责创建HttpProcessor实例,并将curProcessors加1
recycle(processor);//将新创建的HttpProcessor对象入栈,这个到说到生命周期再说
}
看看newProcessor()方法的实现:
org.apache.catalina.connector.http.HttpConnector
/**
* The set of processors that have ever been created.
*/
private Vector created = new Vector();
/**
* 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 processor = new HttpProcessor(this, curProcessors++);
if (processor instanceof Lifecycle) {
try {
((Lifecycle) processor).start();//调用processor的start()方法,注意每一个HttpProcessor都运行在它们自己的线程里面
} catch (LifecycleException e) {
log("newProcessor", e);
return (null);
}
}
created.addElement(processor);//把processor放入vector容器中
return (processor);
}
大多数时间里,createProcessor()方法并不会创建一个新的HttpProcessor实例,相反,它会从池中获得一个对象。如果栈中还有一个HttpProcessor实例可以使用,createProcessor()方法就从栈中获取一个对象,将其返回,如果栈中已经空了,已经创建的HttpProcessor实例的数量还没有超过限定的最大值,createProcessor()方法就会创建一个新的HttpProcessor实例。但是,若HttpProcessor实例的数量已经达到了限定的最大值,createProcessor()方法返回null.此时,服务器会简单的关闭套接字。如果createProcessor()方法的返回值不为null,则会将客户端套接字传入到HttpProcessor类的assgin()方法中。还有,特别注意到HttpConnection与HttpProcesstor的关系。
那么第四点注意的可想而知就是processor.assgin(socket)这个方法了。
org.apache.catalina.connector.http.HttpProcessor
/**
* The socket we are currently processing a request for. This object
* is used for inter-thread communication only.
*/
private Socket socket = null;
/**
* Is there a new socket available?
*/
private boolean available = false;
// -------------------------------------------------------- Package Methods
/**
* 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
while (available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Store the newly available Socket and notify our thread
this.socket = socket;
available = true;
notifyAll();
if ((debug >= 1) && (socket != null))
log(" An incoming request is being assigned");
}
这个方法就是做了一个很简单的是,就是把把传入来的socket对象赋值给HttpProcessor的成员变量socket。现在,HttpProcessor实例的任务就是读取套接字的输入流,解析HTTP请求。这里需要注意的是,assign()方法直接返回,而不要等待HttpProcessor实例完成解析,这样HttpConnector才能接续服务传入的HTTP请求。也就是说assign()方法是运行在HttpConnection实例的一个线程中,而解析HTTP请求是运行在HttpProcessor实例中的线程中。这种做法是十分有好处的。把接收客户和处理解析给分开了。提高了效率。那么我们来看看HttpProcessor实例中的run()方法
org.apache.catalina.connector.http.HttpProcessor
// ---------------------------------------------- Background Thread Methods
/**
* 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);//将HttpProcess实例压回栈中
}
// Tell threadStop() we have shut ourselves down successfully
synchronized (threadSync) {
threadSync.notifyAll();
}
}
这个run()方法是在HttpConnection对象创建HttpProcessor实例后就调用其start()方法的,看看这个线程做了什么事:获取套接字,进行处理,调用连接器的recycle()方法将当前的HttpProcessor实例压回栈中。由此看出assgin()方法是获取HttpConnection实例传过来的socket实例,而await()方法是一旦assgin()方法获取到了socket实例后,那么await()能够马上得到传过来的socket实例,并且把给保存在自己的线程中,assgin()和await()这两个方法是运行在不同的两个线程中,那么问题来了,Tomcat是怎么处理它们之间的逻辑关系的??
这里得意于两个方法和一个boolean变量:java.lang.Object类的wait()方法和notifyAll()方法和available变量。
wait()方法会使当前线程进入等待状态,直到其他线程调用了这个对象的notify()方法和notifyAll()方法。那么来分析以下assgin()方法和await()方法的具体实现。
org.apache.catalina.connector.http.HttpProcessor
// -------------------------------------------------------- Package Methods
/**
* Is there a new socket available?
*/
private boolean available = false;
/**
* 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
while (available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Store the newly available Socket and notify our thread
this.socket = socket;
available = true;
notifyAll();
if ((debug >= 1) && (socket != null))
log(" An incoming request is being assigned");
}
// -------------------------------------------------------- Private Methods
/**
* Await a newly assigned Socket from our Connector, or <code>null</code>
* if we are supposed to shut down.
*/
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();
if ((debug >= 1) && (socket != null))
log(" The incoming request has been awaited");
return (socket);
}
当“处理器线程”刚刚启动时,available变量的值为false,假如现在有用户连接上来,那么assgin()方法中由于available值为false,所以它会把socket赋值给this.socket;而对于await()方法来说,在这一段时间内,由于!available为真,那么await()方法中的while()方法会一直阻塞,如果这时assgin()方法中把available的值修改为true时,并且调用notifyAll()方法,那么将继续执行await()方法,这时available这时为true,所以退出循环体,那么这时就执行socket = this.socket,await()方法拿到了socket实例了,接着又把available修改成false。
这里又有两个小问题了:
1,为什么await()方法要使用一个局部变量socket呢?而不是直接将成员变量socket返回呢?
因为使用局部变量可以在当前Socket对象处理完之前,继续接收下一个Socket对象。
2,为什么await()方法需要调用notifyAll()方法?
是为了防止出现另一个Socket对象已经到达,而此时变量available的值还是true的情况。在这种情况下,“连接器”会在assgin()方法内循环中阻塞,直到“处理器”调用了notifyAll()方法。