本文介紹基于tomcat7中默認(rèn)的BIO方式
以前說過tomcat通過socket傳輸 接口請(qǐng)求之路
發(fā)送數(shù)據(jù)
大部分的HTTP請(qǐng)求一般都是長(zhǎng)連接,可以從HTTP請(qǐng)求頭中看到
一般發(fā)送HTTP請(qǐng)求之前會(huì)建立一個(gè)socket連接匀奏。如果tomcat判斷需要對(duì)這個(gè)socket請(qǐng)求進(jìn)行關(guān)閉鞭衩,會(huì)在HTTP請(qǐng)求的響應(yīng)頭中寫入Connection:close,當(dāng)HTTP得到這樣的響應(yīng)就會(huì)關(guān)閉對(duì)應(yīng)的連接娃善。如果tomcat判斷這個(gè)連接可以繼續(xù)使用,保持長(zhǎng)連接论衍,響應(yīng)頭就不設(shè)置對(duì)應(yīng)的內(nèi)容,則會(huì)繼續(xù)使用Connection:keep-alive聚磺。
注意坯台,如果關(guān)閉的話是http發(fā)起關(guān)閉,而不是tomcat進(jìn)行關(guān)閉
長(zhǎng)連接表示請(qǐng)求完成后socket連接不關(guān)閉瘫寝,還可以繼續(xù)通過這個(gè)對(duì)應(yīng)的socket連接繼續(xù)發(fā)送請(qǐng)求蜒蕾。
長(zhǎng)連接請(qǐng)求和返回
發(fā)送http請(qǐng)求前需要建立一個(gè)socket連接稠炬,長(zhǎng)連接表示請(qǐng)求完成后socket連接不關(guān)閉,還可以通過這個(gè)socket連接繼續(xù)發(fā)送請(qǐng)求咪啡。如果tomcat如果這個(gè)socket請(qǐng)求需要關(guān)閉首启,那么http響應(yīng)頭中Connection:close。這樣接收完響應(yīng)之后就會(huì)關(guān)閉socke連接撤摸,如果保持長(zhǎng)連接毅桃,http響應(yīng)頭就不設(shè)置。
接收數(shù)據(jù)
-
Tomcat通過Processor(所有協(xié)議處理器的通用接口)處理請(qǐng)求准夷。
- Processor內(nèi)部會(huì)設(shè)置Endpoint钥飞,并通過Endpoint的內(nèi)部類Acceptor接收socket連接。
- 客戶端發(fā)送數(shù)據(jù)衫嵌,在服務(wù)端的操作系統(tǒng)中有個(gè)RecvBuf(緩沖區(qū))读宙,操作系統(tǒng)會(huì)將數(shù)據(jù)先放在recvBufferbyte中,如果recvbuf中填滿了楔绞,就無(wú)法發(fā)送數(shù)據(jù)了结闸。每個(gè)socket對(duì)應(yīng)一個(gè)緩沖區(qū)。當(dāng)客戶端寫socket的發(fā)送數(shù)據(jù)是墓律,也會(huì)放在緩沖區(qū)sendbuf中膀估。
參考如下源碼內(nèi)容:
- AbstractHttp11Processor類中的processSocketWapper方法
- JIOEndpoint類中有個(gè)Acceptor方法幔亥,會(huì)通過BIO的方式接受socket
- Socket=serverSocketFactory.accept
- setSocketOption設(shè)置socket參數(shù)的方法中有個(gè)soTimeout參數(shù)耻讽,這個(gè)參數(shù)是指當(dāng)socke從recvbuf中read數(shù)據(jù)時(shí),如果recvbuf中數(shù)據(jù)為空帕棉,則read阻塞针肥,具體阻塞時(shí)間就是這個(gè)參數(shù)指定的時(shí)間。
- 第一次從socket中獲取到數(shù)據(jù)寫入InputBuffer中香伴,然后進(jìn)行解析對(duì)應(yīng)的請(qǐng)求行(請(qǐng)求頭)慰枕,請(qǐng)求方法,請(qǐng)求協(xié)議等放入到Request中即纲,并且設(shè)置一些參數(shù)具帮。
處理socket
- BIO的方式,每接收到一個(gè)socket就將其交給一個(gè)線程低斋,在tomcat的BIO里是每個(gè)請(qǐng)求都有一個(gè)對(duì)應(yīng)的線程進(jìn)行處理蜂厅,當(dāng)socket關(guān)閉后,這個(gè)線程也會(huì)被釋放膊畴。
- NIO則是一個(gè)線程處理多個(gè)請(qǐng)求掘猿。
參考如下源碼內(nèi)容:
- JIoEndpoint類中的prcocessSocket方法的getExecutor.execute
- 在BIO的方式中最大請(qǐng)求數(shù)maxConnection和最大線程數(shù)MaxThread實(shí)際是一致的
- 在調(diào)用線程處理時(shí)根據(jù)run方法的process調(diào)用中的的內(nèi)容,找到AbstarctProtocol中的process方法唇跨,方法會(huì)發(fā)現(xiàn)首先要調(diào)用createProcessor創(chuàng)建一個(gè)處理稠通,根據(jù)不同的實(shí)現(xiàn)類里面設(shè)置了不同的屬性衬衬,這個(gè)屬性也可以通過<Connector>標(biāo)簽來設(shè)置。maxKeepAliveRequests屬性改橘,表示這個(gè)長(zhǎng)連接上能夠處理的最大活動(dòng)數(shù)(http請(qǐng)求)默認(rèn)100
- 創(chuàng)建處理器完成后滋尉,調(diào)用對(duì)應(yīng)process方法,解析http請(qǐng)求中的數(shù)據(jù)飞主。
- 如果maxKeepAliveRequests==1兼砖,那么處理完后連接就會(huì)被關(guān)閉,意味著對(duì)應(yīng)的設(shè)置keepalive=false
- 對(duì)應(yīng)的判斷既棺,如讽挟,當(dāng)前活躍的線程數(shù)占線程池最大線程數(shù)的比例大于 75%,那么則關(guān)閉KeepAlive丸冕,不再支持長(zhǎng)連接耽梅,不支持長(zhǎng)連接并不表示不支持連接處理,意味著75%之后的ssocket連接將會(huì)被當(dāng)成短鏈接處理胖烛。但是當(dāng)超過100%的socket連接數(shù)時(shí)眼姐,將不再處理后續(xù)的socke連接。
- getInputBuffer().parseRequest和getInputBuffer().parseHeaders()讀取請(qǐng)求體和請(qǐng)求頭
- 調(diào)用adapter進(jìn)行服務(wù)處理佩番,調(diào)用fill等一系列方法众旗。
fill方法是將操作系統(tǒng)中的revfbuf讀取到tomcat的緩沖區(qū)buf中(默認(rèn)8kb)
- 處理過程中會(huì)將對(duì)應(yīng)的內(nèi)容放入響應(yīng)response中,返回給客戶端趟畏。同樣的也是先放在緩沖區(qū)贡歧,最后將緩沖區(qū)的數(shù)據(jù)返回。
- 最后判斷數(shù)據(jù)是否響應(yīng)完成赋秀,把剩余的數(shù)據(jù)清除完利朵,獲取下個(gè)請(qǐng)求。
public SocketState process(SocketWrapper<S> socketWrapper)
throws IOException {
RequestInfo rp = request.getRequestProcessor();
// 設(shè)置請(qǐng)求狀態(tài)為解析狀態(tài)
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
// 設(shè)置IO
setSocketWrapper(socketWrapper);
//設(shè)置輸入猎莲、輸出緩沖區(qū)
//將socket的InputStream與InternalInputBuffer進(jìn)行綁定(緩沖區(qū)內(nèi)容)
getInputBuffer().init(socketWrapper, endpoint);
// 將socket的OutputStream與InternalOutputBuffer進(jìn)行綁定
getOutputBuffer().init(socketWrapper, endpoint);
// 長(zhǎng)連接等一系列標(biāo)志
keepAlive = true;
// NioEndpoint返回true, BIO返回false
if (endpoint.getUsePolling()) {
keptAlive = false;
} else {
keptAlive = socketWrapper.isKeptAlive();
}
// 如果當(dāng)前活躍的線程數(shù)占線程池最大線程數(shù)的比例大于75%(默認(rèn)值)绍弟,那么則關(guān)閉KeepAlive,不再支持長(zhǎng)連接
if (disableKeepAlive()) {
socketWrapper.setKeepAliveLeft(0);
}
// keepAlive的值會(huì)從請(qǐng)求中讀取著洼,默認(rèn)為true
while () {
// keepAlive如果為true,接下來需要從socket中不停的獲取http請(qǐng)求
// 解析請(qǐng)求頭
try {
// 第一次從socket中讀取數(shù)據(jù)樟遣,并設(shè)置socket的讀取數(shù)據(jù)的超時(shí)時(shí)間
// 對(duì)于BIO,一個(gè)socket連接建立好后身笤,不一定馬上就被Tomcat處理了豹悬,其中需要線程池的調(diào)度,所以這段等待的時(shí)間要算在socket讀取數(shù)據(jù)的時(shí)間內(nèi)展鸡;而對(duì)于NIO而言屿衅,沒有阻塞
//第一次連接的時(shí)候用,超時(shí)時(shí)間=真實(shí)的時(shí)間-socket建立的時(shí)間
setRequestLineReadTimeout();
// 解析請(qǐng)求行
if (!getInputBuffer().parseRequestLine(keptAlive)) {
}
if (endpoint.isPaused()) {
//如果Endpoint被暫停了莹弊,則返回503
} else {
keptAlive = true;
// 每次處理一個(gè)請(qǐng)求就重新獲取一下請(qǐng)求頭和cookies的最大限制
request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());//默認(rèn)100
request.getCookies().setLimit(getMaxCookieCount());//默認(rèn)200 }
}
//如果最大的保持連接請(qǐng)求數(shù)量==1涤久,表示只允許一次請(qǐng)求
if (maxKeepAliveRequests == 1) {
keepAlive = false;//長(zhǎng)連接參數(shù)直接設(shè)為false
} else if (maxKeepAliveRequests > 0 &&
socketWrapper.decrementKeepAlive() <= 0) {
// 如果已經(jīng)達(dá)到了keepAlive的最大限制涡尘,也設(shè)置為false,則不會(huì)繼續(xù)從socket中獲取Http請(qǐng)求了
keepAlive = false;
}
//在適配器開始處理請(qǐng)求
if (!getErrorState().isError()) {
try {
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE); // 設(shè)置請(qǐng)求狀態(tài)為服務(wù)狀態(tài)响迂,表示正在處理請(qǐng)求
adapter.service(request, response); // 處理請(qǐng)求
}
}
// 完成請(qǐng)求處理
rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT); // 設(shè)置請(qǐng)求的狀態(tài)為處理請(qǐng)求結(jié)束
if (!isAsync() && !comet) {
// 當(dāng)前http請(qǐng)求已經(jīng)處理完了考抄,做一些收尾工作
endRequest();
}
rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT); // 請(qǐng)求狀態(tài)為輸出結(jié)束
if (!isAsync() && !comet || getErrorState().isError()) {
if (getErrorState().isIoAllowed()) {
// 準(zhǔn)備處理下一個(gè)請(qǐng)求
getInputBuffer().nextRequest();
getOutputBuffer().nextRequest();
}
}
rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
// 如果處理完當(dāng)前這個(gè)Http請(qǐng)求之后,發(fā)現(xiàn)socket里沒有下一個(gè)請(qǐng)求了,那么就退出當(dāng)前循環(huán)
// 如果是keepalive蔗彤,就不會(huì)關(guān)閉socket, 如果是close就會(huì)關(guān)閉socket
// 對(duì)于keepalive的情況川梅,因?yàn)槭且粋€(gè)線程處理一個(gè)socket,當(dāng)退出這個(gè)while后,當(dāng)前線程就會(huì)介紹然遏,
// 當(dāng)時(shí)對(duì)于socket來說贫途,它仍然要繼續(xù)介紹連接,所以又會(huì)新開一個(gè)線程繼續(xù)來處理這個(gè)socket
if (breakKeepAliveLoop(socketWrapper)) {
break;
}
}
rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
}