一、tomcat組成

tomcat中有一個Engine會包含多個Host(類似List<Host>),請求從Engine到Host會流經Pipeline,Pipeline中有多個Valve,Valve會處理一些事情;然后從Host到Context,再到Wrapper,最后才到Servlet;沒到一級,中間就會經過Pipeline;怎么理解Pipeline呢,類似于一個Valve的集合,Valve是具體的處理方式,和過濾器差不多,我們也可以自定義,一般不會這么做,沒必要在tomcat層去處理請求;
二、接受數(shù)據流程
1.以BIO的Http請求為例

當我們在server.xml中配置好協(xié)議后,根據全限定名去查找協(xié)議解析策略;

當Http11Protocol初始化時;會初始化JIoEndpoint,JIoEndpoint中包含兩個處理請求的內部線程,Acceptor和SocketProcessor;
Acceptor:接受Socket,建立連接,一個協(xié)議只有一個線程在跑,接收到連接后,交給SocketProcessor線程池,處理請求,SocketProcessor線程池的大小,決定了服務器的并發(fā)量;
SocketProcessor:具體處理請求,包括接受數(shù)據,處理數(shù)據,響應數(shù)據;


源碼中serverSocketFactory.acceptSocket,就是調用socket的accept方法,建立連接,然后再processSocket方法中處理連接;

processSocket方法,主要是把建立的Socket包裝成SocketProcessor,然后異步去處理Socket連接;



主要是handler.process方法;這個方法代碼太多,就不截取了,主要是看state = processor.process(wrapper);這一行,去處理響應的請求;到

先去處理讀取超時時間

如果會減去排隊時間,然后再重新設置讀取超時時間

后面就是依次解析請求行,請求頭

解析主要是看fill方法

fill方法后面再細說,再回到AbstractHttp11Processor.process

adapter就是處理完請求頭和請求行之后,后續(xù)處理請求的方法
三.fill方法說明

該方法主要是處理AbstractInputBuffer.buf緩沖流屬性,把操作系統(tǒng)的數(shù)據inputStream讀到buf中去,InternalInputBuffer中幾個重要屬性:
pos:當前處理位置
lastValid:當前接受數(shù)據位置
end:請求頭標記結束位置
該方法,把數(shù)據從pos位置放入,一直放入完成后,然后后移lastValid位置;每次調用fill方法時,都會把pos位置移到lastValid位置才會去再次調用;
1.讀請求頭
讀請求頭就是把inputStream讀到上,再把lastValid后移相應的長度;
2.讀請求體
讀請求體,如果請求頭讀完剩下的長度不夠4500則重新new 一個buf(緩沖區(qū));然后把pos和lastValid位置移到end位置,意思是請求頭后面的緩沖區(qū),請求體會重復利用該位置,取一次就會重新再從end位置開始放數(shù)據;目的是為了節(jié)省空間;并且請求體tomcat不會解析,所以這部分數(shù)據交給應用來保存,不需要tomcat保存;(需要明白的一點是,每次進入這個fill方法時,說明在buf上的所有數(shù)據已經被讀取完成,才會進入該方法,如果在第一次讀請求體時,可能讀最后一個請求行的時候,已經把請求體的前面一部分給讀到buf上去了,這個時候如果再進入這個方法,說明讀請求頭時讀到buf上的數(shù)據,已經被應用給取走了,被覆蓋了也沒關系);
四.ByteChunk屬性
request對象主要是操作org.apache.coyote.Request,它的大部分屬性,比如method,unparsedURIMB都是記錄ByteChunk,主要是有三個屬性
buff:指向AbstractInputBuffer.buf,操作具體的那個緩沖流
start:開始位置:
end:結束位置
該方法主要是為了節(jié)約性能,在解析時沒有去真的解析,只是記錄了當前屬性在緩沖流中的位置,等真正用到時,再去解析成我們需要的格式;包括后續(xù)去讀請求體也差不多;真正到了Servlet處理時,才去處理解析請求體
五.doRead方法

doRead方法,主要在Servlet調用讀請求體時去讀取數(shù)據,remaining屬性代表還剩余多少數(shù)據可讀;當剩余還有剩余數(shù)據,則去讀取,當讀到的數(shù)據多余剩余數(shù)據時,就只從開始位置,讀取剩余數(shù)據長度(縮小chunk塊),然后把remaining減去真正讀到的數(shù)據,這個時候會變成負數(shù),這個負數(shù)會在最后end方法中去處理,如果end為負數(shù)的話,pos標記位會網前移動相應的位置,相當于會移動到這個請求體結束的位置,然后去處理下個請求;
六.end方法

end方法在處理一次http請求完成后執(zhí)行,該方法主要是為了把請求體讀完,和doRead類似,這里就能看到,如果doRead的remaining屬性為負數(shù)的話,則這個方法出棧后,就會pos就會被剪掉相應的長度;