Tomcat4默認連接器
tomcat連接器是一個獨立的模塊,可以插入到一個servlet容器。一個tomcat連接器必須符合以下要求:
- 必須實現org.apache.catalina.Connector接口
- 必須創建一個實現org.apache.catalina.Request接口的request對象
- 必須創建一個實現org.apache.catalina.Response接口的response對象
Container接口的invoke方法:
/**
* Process the specified Request, and generate the corresponding Response,
* according to the design of this particular Container.
*
* @param request Request to be processed
* @param response Response to be produced
*
* @exception IOException if an input/output error occurred while
* processing
* @exception ServletException if a ServletException was thrown
* while processing this request
*/
public void invoke(Request request, Response response)
throws IOException, ServletException;
在invoke方法中,容器加載servlet類、調用service方法、管理session、記錄錯誤信息日誌等等。
使用對象池來降低複雜對象的創建開銷。
*HTTP1.1新特性
- 持久連接
在HTTP1.0中,每對Request/Response都使用一個新的連接。
HTTP 1.1則支持Persistent Connection, 並且默認使用persistent connection.
connection: keep-alive
- 塊編碼
HTTP1.1支持chunked transfer,所以可以有Transfer-Encoding頭部域,HTTP1.0則沒有。
Transfer-Encoding: chunked
1D\r\n I'm as helpless as a kitten u 9\r\n p a tree. 0\r\n
- 狀態100的使用
100 (Continue) 狀態代碼的使用,允許客戶端在發request消息body之前先用request header試探一下server,看server要不要接收request body,再決定要不要發request body。
客戶端在Request頭部中包含Expect: 100-continue
Server看到之後呢如果回100 (Continue) 這個狀態代碼,客戶端就繼續發request body。
HTTP/1.1 100 Continue
HttpConnector類
(1)如何創建一個server socket?
(2)如何維護HttpProcessor池?
(3)如何處理Http請求?
org.apache.catalina.connetor.http.HttpConnector類,實現了org.apache.catalina.Connector、java.lang.Runnable和org.apache.catalina.LifeCycle接口。LifeCycle接口用來維護每一個實現了此接口的Catalina組件的生命週期。
- 創建一個server socket
- private ServerSocket open() throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException,
- UnrecoverableKeyException, KeyManagementException {
- // 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));
- } catch (BindException be) {
- throw new BindException(be.getMessage() + ":" + address + ":" + port);
- }
- } catch (Exception e) {
- log(sm.getString("httpConnector.noAddress", address));
- try {
- return (factory.createSocket(port, acceptCount));
- } catch (BindException be) {
- throw new BindException(be.getMessage() + ":" + port);
- }
- }
- }
- 如何維護HttpProcessor池
首先採用棧來存儲HttpProcessor實例,HttpProcessor池動態擴容,根據三個屬性來設置:
curProcessors: 當前HttpProcessor實例的個數
minProcessors : int 初始化時,最小的HttpProcessor實例個數
maxProcessors:最大HttpProcessor實例個數,當小於0時,不做限制
初始化時創建最小HttpProcessor實例個數代碼:
- // Create the specified minimum number of processors
- while (curProcessors < minProcessors) {
- if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
- break;
- HttpProcessor processor = newProcessor();
- recycle(processor);
- }
- 處理http請求
HttpConnector類在自己的run方法中有自己的主要邏輯,直到HttpConnetor停止之前都會一直等待接收http請求。對於每一個http請求,通過調用createProcessor方法獲得一個HttpProcessor實例。然而,大多數時間,createProcessor方法並不會創建一個新的HttpProcessor對象,而是從一個HttpProcessor池中獲取。如果在這個池中(實際採用的是堆棧來存儲)有一個HttpProcessor實例可供使用,執行出棧操作。如果棧爲空且仍然沒有超過HttpProcessor實例的最大個數,則創建一個新的HttpProcessor實例,否則,createProcessor方法將返回null,此時socket執行關閉操作而不會響應到來的http請求。如果createProcessor方法沒有返回null,客戶端的socket傳遞給HttpProcessor的assign方法。
- private HttpProcessor createProcessor() {
- synchronized (processors) {
- if (processors.size() > 0) {
- return ((HttpProcessor) processors.pop());
- }
- if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {
- return (newProcessor());
- } else {
- if (maxProcessors < 0) {
- return (newProcessor());
- } else {
- return (null);
- }
- }
- }
- }
HttpProcessor類
在本章,我們最感興趣的是HttpProcessor類如何使assign方法異步以便HttpConnetor實例可以同時爲多個http請求服務。HttpProcessor類中另外一個重要的方法是私有方法process方法,它解析了http請求並且調用了容器的invoke方法。
第三章中,HttpConnetor在自己的線程中運行,然而它必須等待當前處理的http請求結束之後纔可以處理下一個請求。
第三章的HttpConnetor類的run方法代碼如下:
public void run() {
ServerSocket serverSocket = null;
int port = 8080;
try {
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
while (!stopped) {
// Accept the next incoming connection from the server socket
Socket socket = null;
try {
socket = serverSocket.accept();
} catch (Exception e) {
continue;
}
// Hand this socket off to an HttpProcessor
HttpProcessor processor = new HttpProcessor(this);
processor.process(socket);
}
}
HttpProcessor類的process方法在第三章中是同步方法。因此,它的run方法等待直到process方法處理結束才接收下一個請求。在本章,默認的連接器的HttpProcessor類實現了Runnable接口,因此每一個HttpProcessor實例都運行在自己的線程中,我們稱爲“processor線程”
Lifecycle接口的start和stop方法
/**
* Prepare for the beginning of active use of the public methods of this
* component. This method should be called before any of the public
* methods of this component are utilized. It should also send a
* LifecycleEvent of type START_EVENT to any registered listeners.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
public void start() throws LifecycleException;
/**
* Gracefully terminate the active use of the public methods of this
* component. This method should be the last one called on a given
* instance of this component. It should also send a LifecycleEvent
* of type STOP_EVENT to any registered listeners.
*
* @exception LifecycleException if this component detects a fatal error
* that needs to be reported
*/
public void stop() throws LifecycleException;
HttpProcessor實現了Lifecycle接口,因此HttpProcessor類的start方法代碼如下:
/**
* 判斷HttpProcessor組件是否啓動
*/
private boolean started = false;
/**
* Start the background thread we will use for request processing.
*
* @exception LifecycleException if a fatal startup error occurs
*/
public void start() throws LifecycleException {
if (started)
throw new LifecycleException
(sm.getString("httpProcessor.alreadyStarted"));
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
threadStart();
}
/**
* 開啓後臺處理線程
*/
private void threadStart() {
log(sm.getString("httpProcessor.starting"));
thread = new Thread(this, threadName);
thread.setDaemon(true);
thread.start();
if (debug >= 1)
log(" Background thread has been started");
}
HttpProcessor的run方法的while循環執行流程:獲得一個socket,處理它,調用connector的recycle(回收)方法把當前的HttpProcessor實例壓回棧中。注意到while循環中停止到await方法處,await方法掌握着“processor thread”的控制流,直到它從HttpConnetor獲得到一個新的socket對象。換句話說,直到HttpConnetor類調用HttpProcessor實例的assign方法。
HttpProcessor processor = createProcessor();
processor.assign(socket);
HttpProcessor的run方法代碼如下:
/* * 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();
}
}
將調用完的HttpProcessor實例壓回棧中的代碼實現:
void recycle(HttpProcessor processor) {
processors.push(processor);
}
然而,await方法和assign方法運行在不同的線程中,assign方法是在HttpConnetor的run方法中被調用的,即“connector線程”。
那麼assign方法是如何告訴await方法它被調用了呢?
利用一個布爾型變量available和java.lang.Object類的wait和notifyAll方法。
注:Object的wait方法導致當前線程等待直到其他線程對這個對象調用notify或者nitifyAll方法。
connetor線程調用的assign方法:
/**
* 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");
}
HttpProcessor線程調用的await方法:
/**
* 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);
}
初始時,當“processor thread”剛啓動時,available爲false,即還沒有可用的socket。因此線程在while循環中等待,直到其他線程調用notify或者notifyAll方法。也就是說,調用wait方法導致“processor thread”暫停直到“connector thread”對這個HttpProcessor實例調用notifyAll方法。當一個新的socket被分配,“connector thread”調用HttpProcessor的assign方法。avilable置爲true,喚醒“processor thread”。
爲什麼await方法需要使用一個本地變量(socket)且不返回這個socket變量的實例?
因爲HttpProcessor實例的socket變量在當前socket處理完之前還可以分配給下一個到來的socket。
爲什麼await方法需要調用notifyAll?
爲了當另外一個socket到達的時候此時available爲true,這時候,“connector thread”將會停止裏面的assign方法直到收到“processor thread”的notifyAll方法。