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