這一篇章,我想介紹一下tomcat容器中的連接器部分,並將tomcat4、tomcat6及tomcat8他們的連接器對比一下,看看做了哪些改進。
1、首先我們再回顧一下模擬tomcat容器這個應用中連接器的代碼
public class Connector implements Runnable {
Container container;
public Container getContainer() {
return container;
}
public void setContainer(Container container) {
this.container = container;
}
@Override
public void run() {
try {
ServerSocket serverSocket = new ServerSocket(8080,
20,
InetAddress.getByName("localhost"));
while (true) {
Socket socket = serverSocket.accept();
Request request = new ServletRequest();
request.setInputStream(socket.getInputStream());
Response response = new ServletResponse();
response.setOutputStream(socket.getOutputStream());
((ServletRequest) request).parseRequest(request);
container.invoke(request, response);
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() {
Thread t = new Thread(this);
t.start();
}
}
連接器的全部工作也就是上面這塊代碼完成的功能,創建
1、服務端套接字
2、監聽端口
3、接收客戶端連接
4、解析報文
5、調用容器
2、下面我們看看tomcat4連接器怎麼處理的
我繼續會處理掉一些此階段不太關注的代碼
public void initialize()
throws LifecycleException {
// 這個字段是防止該對象重複初始化
if (initialized)
throw new LifecycleException (
sm.getString("httpConnector.alreadyInitialized"));
this.initialized=true;
// Establish a server socket on the specified port
serverSocket = open();
}
// open創建一個ServerSocket
private ServerSocket open()
throws IOException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException,
KeyManagementException
{
// 獲取ServerSocket工廠
ServerSocketFactory factory = getFactory();
// If no address is specified, open a connection on all addresses
if (address == null) {
try {
return (factory.createSocket(port, acceptCount));
} catch (BindException be) {
throw new BindException(be.getMessage() + ":" + port);
}
}
}
可見初始化操作創建了一個ServerSocket套接字
@Override
public void start() throws LifecycleException {
// Validate and update our current state
if (started)
throw new LifecycleException
(sm.getString("httpConnector.alreadyStarted"));
threadName = "HttpConnector[" + port + "]";
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
// 創建一個後臺線程,connector是一個Runnable實現類,這個函數就是啓動了線程
threadStart();
// 此處創建了一個Processor對象池,每一個Processor是一個線程,newProcessor的時候順便也啓動了線程,所以我們接下來分別看看兩個線程做的什麼
while (curProcessors < minProcessors) {
if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
break;
HttpProcessor processor = newProcessor();
recycle(processor);
}
}
我們來看看HttpConnector類定義,可以發現其實現了一個Runnable接口,threadStart這個函數就是啓動了HttpConnector的線程
tomcat4這個時候jdk還沒有實現線程池,這裏是通過棧實現了一個對象池,對象池裏存放的是Processor,newProcessor的時候,順便啓動了線程,接下來我們看看HttpConnector的run和HttpProcessor的run
HttpConnector
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();
// 分配一個空閒的HttpProcessor
HttpProcessor processor = createProcessor();
// 給processor分配工作
processor.assign(socket);
// Notify the threadStop() method that we have shut ourselves down
// if (debug >= 3)
// log("run: Notifying threadStop() that we have shut down");
synchronized (threadSync) {
threadSync.notifyAll();
}
}
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();
}
HttpProcessor
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();
}
}
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);
}
connector接收到一個新的連接,從對象池中獲取一個Processor對象,並將socket分配給對象,而Processor,如果沒有新的要處理的業務,會阻塞在await等待一個新的socket,這裏是典型的等待通知的應用。
Connector接收到新的請求,assign(Socket)將新請求的socket分配給processor,這裏用到了synchronized同步處理,且synchronized保證了裏面內存區域數據在connector線程與processor之間的可見性。將socket傳給了processor之後,將available賦值爲true,並通知,這時候processor取消阻塞,獲取到新的socket,並調用process。
private void process(Socket socket) {
boolean ok = true;
boolean finishResponse = true;
SocketInputStream input = null;
OutputStream output = null;
// Construct and initialize the objects we will need
try {
input = new SocketInputStream(socket.getInputStream(),
connector.getBufferSize());
} catch (Exception e) {
log("process.create", e);
ok = false;
}
keepAlive = true;
while (!stopped && ok && keepAlive) {
finishResponse = true;
try {
request.setStream(input);
request.setResponse(response);
output = socket.getOutputStream();
response.setStream(output);
response.setRequest(request);
((HttpServletResponse) response.getResponse()).setHeader
("Server", SERVER_INFO);
} catch (Exception e) {
log("process.create", e);
ok = false;
}
// Parse the incoming request
try {
if (ok) {
parseConnection(socket);
parseRequest(input, output);
if (!request.getRequest().getProtocol()
.startsWith("HTTP/0"))
parseHeaders(input);
if (http11) {
// Sending a request acknowledge back to the client if
// requested.
ackRequest(output);
// If the protocol is HTTP/1.1, chunking is allowed.
if (connector.isChunkingAllowed())
response.setAllowChunking(true);
}
}
}
// Ask our Container to process this request
try {
((HttpServletResponse) response).setHeader
("Date", FastHttpDateFormat.getCurrentDate());
if (ok) {
connector.getContainer().invoke(request, response);
}
}
// Finish up the handling of the request
if (finishResponse) {
try {
response.finishResponse();
} catch (IOException e) {
ok = false;
} catch (Throwable e) {
log("process.invoke", e);
ok = false;
}
try {
request.finishRequest();
} catch (IOException e) {
ok = false;
} catch (Throwable e) {
log("process.invoke", e);
ok = false;
}
try {
if (output != null)
output.flush();
} catch (IOException e) {
ok = false;
}
}
if ( "close".equals(response.getHeader("Connection")) ) {
keepAlive = false;
}
// End of request processing
status = Constants.PROCESSOR_IDLE;
// Recycling the request and the response objects
request.recycle();
response.recycle();
}
try {
shutdownInput(input);
socket.close();
} catch (IOException e) {
;
} catch (Throwable e) {
log("process.invoke", e);
}
socket = null;
}
process()就是接收數據,解析http請求,調用容器,這裏沒有做什麼特殊設計。
所以我這裏推薦大家讀源碼的話,版本選擇tomcat4,這個版本沒有過度設計,對於通過讀源碼深入瞭解tomcat再合適不過。
3、我們再來看看tomcat6的連接器
這裏就不講解代碼了,可以按照這個思路去看下tomcat6的源碼,我這裏講解一下tomcat6連接器的變化部分與目的。
這裏針對連接的創建與監聽,報文的處理及調用容器均做了重構,添加了EndPoint、Processor、Adapter三個模塊。
EndPoint:負責與客戶端的對接,在系統的設計中,這部分是變化的,tomcat6就有加入NIO非阻塞 I/O 多路複用,tomcat8中還加入了異步IO,
ProtocolHandler:負責協議的解析,Http11Protocol負責的是http協議,
Adapter:通過適配器調用容器,適應不同協議下輸出到容器的數據格式是一致的。
上面三個模塊的重構封裝符合設計中的開閉原則(開放擴展,關閉修改)
後期我會針對tomcat6的連接器做一次詳細的描述。