——每天的寥寥幾筆,堅持下去,將會是一份沉甸甸 的積累。
今天的筆記是針對《tomcat how works》第四章——tomcat的默認連接器來講的。
1.在上一篇文章我羅列書第三章的源碼目錄,如下:
connector: RequestStream,ResponseStream,ResponseWriter
connnectot.http:HttpConnector,HttpProcessor//前者用於創建serverSocket,監聽客戶端請求;後者是個關鍵類,後面單獨講
HttpRequest,HttpResponse,HttpRequestFacade,HttpResponseFacade
//request和response實現了HttpRequestServlet和HttpResponseServlet接口,相關填充數據由下面兩個對象提供
HttpHeader,HttpRequestLine//HTTP消息的頭部,請求行封裝出的兩個實體類
SocketInputStream//用Socket.InputStream封裝出的新的類,實現了讀取HTTP消息的頭部和請求行,封裝到上面兩個對象中
Constants//存一些常量,降低耦合度,便於修改
core: servletProcessor,staticResourceProcessor
//這兩個類爲業務處理類,就servlet而言,會根據connector解析出的處理類類名,用反射機制實例化某servlet類來處理。
其實每章就是在之前簡單的基礎上進行擴張充實。在第四章裏,覆蓋了我們自己寫的HttpConnector,HttpProcessor,採用了org.apache.catalina.connector.http.HttpConnector和org.apache.catalina.connector.http.HttpProcessor(實現了多處理器線程,可以用來處理不同的客戶端請求);並新增了一個處理servlet的容器SimpleContainer。【源碼及書籍下載鏈接:http://pan.baidu.com/s/1ntBhXX3】
2.再強調一下,第四章的突破點在於:“多處理線程”,所以需要多線程的相關知識,不是很熟的,建議先給自己充下電。馬上,下一篇我也會提供相關筆記的。
3.接下來把處理請求的執行流程給梳理下。
(1)bootstrap類啓動
HttpConnector connector = new HttpConnector();
SimpleContainer container = new SimpleContainer();
connector.setContainer(container);
try {
connector.initialize();
connector.start();
解釋:
- 創建一個Httpconnector連接器,set一個container容器(這個後面解釋);
- 初始化操作:會調用open()函數,而open()函數會調用DefaultServerFactory來生產serverSocket對象(前三章都是直接new出來的,這裏用到了工廠)
- start操作:會new出來5個(默認,等於minProcessors)httpProcessor對象(通過調用newProcessor()函數,這個函數會啓動httpProcessor自身的線程,即run函數已經跑起來),然後添加到棧中(源碼如下)。
start():
while (curProcessors < minProcessors) {
if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
break;
HttpProcessor processor = newProcessor();
recycle(processor);//加入棧中
}
newProcessor():
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();//httpProcessor實現了runnable接口,調用start函數直接啓動線程,具體源碼後面提到
} catch (LifecycleException e) {
log("newProcessor", e);
return (null);
}
}
created.addElement(processor);
return (processor);
}
(2)其實(1)中的connector.start()還會進行一個操作,就是啓動httpConnector線程(這個類也實現了runnable接口)。於是自然而然,start函數一執行,便進入run函數
//極簡模式
public void run() {
while (!stopped) {
//監聽客戶端請求
//獲取socket對象
processor.assign(socket);//將Socket對象傳到httpProcessor裏面處理
}
}
(3)httpProcessor類。主要功能依舊是封裝request,response對象的相關數據,用於觸發相關servlet的service方法。封裝數據的過程,也就涉及http消息解析,前一篇文章講到了parseRequest,parseHeaders等,在本文中該類將繼續拓展,新增瞭如parseAcceptLanguage等。
除了上面提到的幾個咱們熟悉的方法之外,又新增了assign()和await()方法,下面詳細介紹(設計的非常巧妙),主要看看代碼裏我的文字介紹
public void run() {
while (!stopped) {
Socket socket = await();
//流程從這開始:
//new出這個processor對象的時候已經觸發run方法(上面講過),然後調用await()方法,由於默認available=false,
//因此await方法調用wait()方法,是的線程進入等待狀態(等待notify方法喚醒)
//知道httpConnector調用assign方法,是的available=true,然後又notify,使得線程被喚醒,正好available=true,跳出while循環,
//available又被賦成false。此時run中的socket不爲null,調用process方法進行處理,最後將封裝好的request和response對象作爲參數觸發serlvet容器的處理程序
if (socket == null)continue;
try {
process(socket);
} catch (Throwable t) {}
connector.recycle(this);
}
}
private void process(Socket socket) {//極簡,刪掉了細節部分
connector.getContainer().invoke(request, response);
}
synchronized void assign(Socket socket) {
while (available) {
try {
wait();
} catch (InterruptedException e) {}
}
this.socket = socket;
available = true;
notifyAll();
}
private synchronized Socket await() {
while (!available) {
try {
wait();
} catch (InterruptedException e) {}
}
Socket socket = this.socket;
available = false;
notifyAll();
return (socket);
}
(4)還有一個SimpleContainer類,這個就是servlet容器,用來接收request,response對象,解析request得到相應的servlet,根據反射機制,觸發改serlvet的service方法
public void invoke(Request request, Response response)throws IOException, ServletException {
servlet.service((HttpServletRequest) request, (HttpServletResponse) response);//主要就是這個invoke方法,和上的process方法掛鉤
}
由於涉及到多線程相關知識,理解起來可能有些難度。下一篇我會整理一份多線程相關的筆記以供大家參考。
又週一了,可能接下來幾天更新頻率不會那麼高(學生黨要上課),不過依舊會盡量保持進度(各種學長阿里,騰訊的offer紛紛到手,學弟甚是欣羨,必須加倍努力)。。。。