很早就聽(tīng)說(shuō)tomcat6使用nio了,這幾天突然想到一個(gè)問(wèn)題,使用nio代替?zhèn)鹘y(tǒng)的bio,ThreadLocal豈不是會(huì)存在沖突?
如果讀者有socket的編程基礎(chǔ),應(yīng)該會(huì)接觸過(guò)堵塞socket和非堵塞socket,堵塞socket就是在accept、read、write等IO操作的的時(shí)候,如果沒(méi)有可用符合條件的資源,不馬上返回,一直等待直到有資源為止。而非堵塞socket則是在執(zhí)行select的時(shí)候,當(dāng)沒(méi)有資源的時(shí)候堵塞,當(dāng)有符合資源的時(shí)候,返回一個(gè)信號(hào),然后程序就可以執(zhí)行accept、read、write等操作,這個(gè)時(shí)候,這些操作是馬上完成,并且馬上返回。而windows的winsock則有所不同,可以綁定到一個(gè)EventHandle里,也可以綁定到一個(gè)HWND里,當(dāng)有資源到達(dá)時(shí),發(fā)出事件,這時(shí)執(zhí)行的io操作也是馬上完成、馬上返回的。一般來(lái)說(shuō),如果使用堵塞socket,通常我們時(shí)開(kāi)一個(gè)線程accept socket,當(dāng)有socket鏈接的時(shí)候,開(kāi)一個(gè)單獨(dú)的線程處理這個(gè)socket;如果使用非堵塞socket,通常是只有一個(gè)線程,一開(kāi)始是select狀態(tài),當(dāng)有信號(hào)的時(shí)候馬上處理,然后繼續(xù)select狀態(tài)。
按照大多數(shù)人的說(shuō)法,堵塞socket比非堵塞socket的性能要好。不過(guò)也有小部分人并不是這樣認(rèn)為的,例如Indy項(xiàng)目(Delphi一個(gè)比較出色的網(wǎng)絡(luò)包),它就是使用多線程+堵塞socket模式的。另外,堵塞socket比非堵塞socket容易理解,符合一般人的思維,編程相對(duì)比較容易。
nio其實(shí)也是類似上面的情況。在JDK1.4,sun公司大范圍提升Java的性能,其中NIO就是其中一項(xiàng)。Java的IO操作集中在java.io這個(gè)包中,是基于流的阻塞API(即BIO,Block IO)。對(duì)于大多數(shù)應(yīng)用來(lái)說(shuō),這樣的API使用很方便,然而,一些對(duì)性能要求較高的應(yīng)用,尤其是服務(wù)端應(yīng)用,往往需要一個(gè)更為有效的方式來(lái)處理IO。從JDK 1.4起,NIO API作為一個(gè)基于緩沖區(qū),并能提供非阻塞O操作的API(即NIO,non-blocking IO)被引入。 BIO與NIO一個(gè)比較重要的不同,是我們使用BIO的時(shí)候往往會(huì)引入多線程,每個(gè)連接一個(gè)單獨(dú)的線程;而NIO則是使用單線程或者只使用少量的多線程,每個(gè)連接共用一個(gè)線程。
這個(gè)時(shí)候,問(wèn)題就出來(lái)了:我們非常多的java應(yīng)用是使用ThreadLocal的,例如JSF的FaceContext、Hibernate的session管理、Struts2的Context的管理等等,幾乎所有框架都或多或少地應(yīng)用ThreadLocal。如果存在沖突,那豈不驚天動(dòng)地?
后來(lái)終于在Tomcat6的文檔(http://tomcat.apache.org/tomcat-6.0-doc/aio.html)找到答案。根據(jù)上面說(shuō)明,應(yīng)該Tomcat6應(yīng)用nio只是用在處理發(fā)送、接收信息的時(shí)候用到,也就是說(shuō),tomcat6還是傳統(tǒng)的多線程Servlet,我畫(huà)了下面兩個(gè)圖來(lái)列出區(qū)別:
tomcat5:客戶端連接到達(dá) -> 傳統(tǒng)的SeverSocket.accept接收連接 -> 從線程池取出一個(gè)線程 -> 在該線程讀取文本并且解析HTTP協(xié)議 -> 在該線程生成ServletRequest、ServletResponse,取出請(qǐng)求的Servlet -> 在該線程執(zhí)行這個(gè)Servlet -> 在該線程把ServletResponse的內(nèi)容發(fā)送到客戶端連接 -> 關(guān)閉連接。
我以前理解的使用nio后的tomcat6:客戶端連接到達(dá) -> nio接收連接 -> nio使用輪詢方式讀取文本并且解析HTTP協(xié)議(單線程) -> 生成ServletRequest、ServletResponse,取出請(qǐng)求的Servlet -> 直接在本線程執(zhí)行這個(gè)Servlet -> 把ServletResponse的內(nèi)容發(fā)送到客戶端連接 -> 關(guān)閉連接。
實(shí)際的tomcat6:客戶端連接到達(dá) -> nio接收連接 -> nio使用輪詢方式讀取文本并且解析HTTP協(xié)議(單線程) -> 生成ServletRequest、ServletResponse,取出請(qǐng)求的Servlet -> 從線程池取出線程,并在該線程執(zhí)行這個(gè)Servlet -> 把ServletResponse的內(nèi)容發(fā)送到客戶端連接 -> 關(guān)閉連接。
從上圖可以看出,BIO與NIO的不同,也導(dǎo)致進(jìn)入客戶端處理線程的時(shí)刻有所不同:tomcat5在接受連接后馬上進(jìn)入客戶端線程,在客戶端線程里解析HTTP協(xié)議,而tomcat6則是解析完HTTP協(xié)議后才進(jìn)入多線程,另外,tomcat6也比5早脫離客戶端線程的環(huán)境。
實(shí)際的tomcat6與我之前猜想的差別主要集中在如何處理servlet的問(wèn)題上。實(shí)際上即使拋開(kāi)ThreadLocal的問(wèn)題,我之前理解tomcat6只使用一個(gè)線程處理的想法其實(shí)是行不同的。大家都有經(jīng)驗(yàn):servlet是基于BIO的,執(zhí)行期間會(huì)存在堵塞的,例如讀取文件、數(shù)據(jù)庫(kù)操作等等。tomcat6使用了nio,但不可能要求servlet里面要使用nio,而一旦存在堵塞,效率自然會(huì)銳降。
所以,最終的結(jié)論當(dāng)然是tomcat6的servlet里面,ThreadLocal照樣可以使用,不存在沖突
大盤(pán)預(yù)測(cè)
國(guó)富論
posted on 2009-06-16 14:30
華夢(mèng)行 閱讀(210)
評(píng)論(0) 編輯 收藏 所屬分類:
JDK