<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    放翁(文初)的一畝三分地

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      210 隨筆 :: 1 文章 :: 320 評論 :: 0 Trackbacks

    引問:NIO在服務端的應用已經被廣為熟悉,但是在客戶端的使用,其實給予的指導并不多。同時在我看來,NIO在客戶端使用就是原來的長連接模式加上事件驅動的框架,而相對于短連接池模式來說,性能是否真的在任何環境都那么突出,其實不然。

     最近正好要優化TB的Cache客戶端,原始代碼是用NIO寫的,但是效率不高,性能也一般,因此反而拖累了服務端的表現,在整個優化過程中,看了NIO2,也就是JDK7中比較突出的AIO,同時也經過反復優化和測試,其中對于NIO應用到客戶端來談一下自己的一些收獲。

    傳統IO操作和NIO操作的區別

     簡單來說:1.對于數據處理由Stream方式轉變稱為了Block方式。2.事件驅動機制替換了傳統的一個線程處理到底的模式。

     第一種轉變不適合需要對于字節流做處理的場景。(需要對字節充分作處理,例如我在另一個優化中對于字節流采用lazy Analysis,通過邊解析邊交驗的方式,提前過濾無效請求,降低由于分析大數據包無效請求帶來的性能消耗)。但是Block傳輸和處理這種轉變符合操作系統的真實模式,使Java可以充分利用各個操作系統的實現來優化性能,同時管道的思想也符合操作系統的真實實現(原來Java都是將雙向通道拆分為In,Out的)。事件驅動使得完整的處理流程被拆分成為流水線作業,最大程度上利用了資源,防止后端處理成為了前端請求的瓶頸,降低了服務器的吞吐量,同時最大限度的給開發者優化流程,縮短關鍵路徑的機會。

     下面表格大致列舉了傳統IO和NIO在客戶端使用的需求和各自的優勢(兩者都需要的就不列入其中了,例如容錯恢復等)

      需求 優勢
    IO(連接池) 1. 連接池的管理。2. 高并發大壓力下Socket數量龐大,對于文件句柄消耗也大 1. 數據發送接受處理簡單,單線程模式。2.可以對字節流逐一解析,避免內存過分無謂消耗
    NIO 1. 交互的協議需要支持會話。(也可以不支持,這就會使得處理模式退化,效率下降,后續會談到)2. 對于接收和發送需要支持多線程,提高效率3. 需要對Channel和Block有所熟悉 1. 資源利用率最大化,性能提升(消息通道的充分利用,操作系統IO優化的使用)2. 充分靈活的將處理分割為多個工作項,流水線作業,減小業務處理對服務器服務請求接收吞吐量的影響。

    NIO在客戶端

    1

    上圖描述的是NIO實現的客戶端和服務端之間的數據交互場景,可以看到由于要最大限度利用消息通道,NIO客戶端需要具備以下幾點:
    1.消息會話支持。
    2.多線程訪問控制。
    3.消息過濾容錯。
    4.超時控制。
    5…..
     消息會話支持,指的是消息的傳輸與接收需要通過通信協議方式來實現會話,NIO在服務端可以很容易使用就是因為,NIO Server自身維護了服務處理會話,而對于客戶端來說,首先上圖可以看到,不同的線程使用NIO Client的時候,發送消息后得到回復的順序并不一定和消息發送的順序一致,因此需要通過在協議中內嵌會話碼,這樣才能夠在結果返回并解析以后通知消息接收者。

     多線程訪問控制,對于消息的發送和接收,可以通過單線程處理,但是這將成為高并發下的處理瓶頸(后續的優化過程會詳細說明)。如果允許多線程發送和接收消息,那么對于發送隊列和接收緩存都將存在著并發訪問控制的需求,同時對于發送和接收在數據需要切分的時候,需要通過小事務來保證數據的一致性。

     消息過濾容錯,由于共享一個數據通道,當返回數據出現問題的時候,如何過濾出錯的數據防止解析異常和死鎖很重要,也是多線程處理不相互影響的保證。

     超時控制,由于共享數據通道是異步服務接收模式,因此請求隊列會成為在高并發下的消耗資源,必要的將隊列中的超時請求刪除,是在網絡異常或者服務端異常的時候,不會被壓垮的保證。

     其他其實還有很多設計細節和要點,這里就不一一列出了。

     歸結起來,如何高效的協調好多線程數據發送和接收及分析是共享數據通道的NIO客戶端最重要的實現關注點。

    NIO客戶端優化分析
     在優化前,客戶端的結構就是最早的傳統的NIO模式,以下兩張圖就可以說明大致的結構。

    2

    3

    第一張圖是NIO的概念模型,第二張圖則是從實際使用的角度去描述了NIO框架中各個角色在實際模型中所處的地位。具體的NIO角色工作介紹就不詳細說了,這里描述一下問題,在高并發下,發現等待消息返回的線程池內的線程不斷堆積,同時處理性能也不斷下降,呈現為惡性循環的狀態。

    4

    傳統模式下,事件處理是單線程串行處理的,例如如果ReadEvent在WriteEvent之前,當ReadEvent出現服務超時或者本身就比較耗時,那么導致其他事件無法得到處理或者處理比較慢。

     初步認定原因:串行化事件處理,導致事件之間處理效率將會相互影響。
     考慮的解決方式:事件處理線程化,將事件觸發與事件處理分離,提高不同事件處理能力。

    5

    從上圖中看到Dispatcher調用Handler處理事件和事件監聽已經剝離開來,這樣應該看起來會很好的解決當前的問題。

     三下五除二,在Dispatcher中新增加了一個Executor(沒有采用Cache的,使用的是fix的,防止資源由于暴漲導致產生大量線程搞垮應用),然后將attachment中的Channel代理類作了線程安全改造。

     測試結果讓我大跌眼鏡,在高并發下不僅沒有提高效率,反而效率降低。仔細觀察后發現,由于對于連接,讀,寫三種事件都做了注冊,在高并發下,讀寫的事件頻度很高,因此會產生大量的線程,同時線程池是fix的(如果是Cache就直接OOM了),在創建線程中消耗的遠大于原來認為是瓶頸的順序執行,加上對于Channel代理的線程安全改造(資源鎖等安全優化),導致最終性能還不抵原來的初始架構。

     看來在Dispatcher中通過線程池方式來剝離框架和業務邏輯并不合適,因此考慮從其他角度入手。從另一方面來考慮,其實如果能夠做到將Read事件和Write事件內部處理作的足夠輕量,就算是順序處理也會有很好的效果,因此就有了下面的設計:
     6

    LightWeightHander是一個輕量級的消息處理Handler,對于原來的業務數據處理做了拆分,達到就算是串行事件處理也能夠防止事件之間相互影響。

     工作拆分實際上成了優化最重要的部分,下面描述了工作拆分的分析點(也是不斷通過測試結果得出的最后的策略)
     7

    上圖是長連接模式中最常見的方式,系統中存在著發送和接收的緩沖區,但是對于發送沒有采用多線程處理,而僅僅采用批量處理(設定一定的閥值,在高并發下緩沖區內容突然增長則采用批量Flush的方式提高效率),寫出不采用多線程其實也是經過測試發現性能在線程創建過程中會有損耗,而且寫的動作消耗時間有限,不需要做此優化。但是對于讀部分,則存在著很多數據分析,拷貝,對象反序列化等操作,因此需要切割工作,支持并行處理,提高反饋速度。這里主要將讀取分析過程切分成三部分,前兩部分都是單線程完成操作,最后一步交由多線程池來執行。

    第一步是由LightWeightHandler通過讀取事件了解有數據需要讀入,然后將數據包分別讀入掛接在讀入隊列中。此處不使用多線程有兩個原因,第一個原因也是線程創建消耗問題,第二個原因是當數據包由于過大需要分包接收,此時采用多線程接收,則會導致消息順序錯亂(消息包根據設定的接收窗口大小讀入,因此沒有編排)。第二步是將讀入的數據包按照報文協議規則劃分邏輯單元(此處需要采用ByteBuffer的各種特性,盡量避免創建新的數據塊和相互拷貝,提高效率)。這部分采用單線程原因是因為此處無法實行流水線作業,本身就是串行的工作,關鍵路徑無法縮短。第三步,將邏輯切分后的數據報文分配給每一個線程執行最耗時的解析和回調工作。

     最后測試效果還是不錯的,總結出來的幾點經驗:

    1.多線程消耗有時候超過你的瓶頸,同時還需要防范在高并發下資源申請的問題。
    2.謹慎分割的任務,將任務處理串行和并行結合起來,找到最短路徑。
    3.NIO處理中充分利用ByteBuffer等Buffer,數據邏輯分段和隔離,盡量少用拷貝和新建方式,提高4.處理速度。(需要注意的是Buffer的相對操作函數使用方式,避免出現Buffer復用導致異常)
    批量處理需要做好測試,找到合理的閥值,防止批量帶來的延時效應和峰值效應。
    5.多線程調試盡量多用打印輸出運行期狀態了解性能瓶頸。(調試就不必了,基本無法確切了解內部高速運轉細節)

    NIO2
     最近也關注了JDK7中的很多信特性,當然NIO2不得不說,這里還是提一下和本文比較相關的AIO。在NIO2中正式的出現了異步IO的接口及實現,對于這種異步模式,框架提供了兩種方式獲取結果:
    采用Future的方式來獲取。
    采用Callback的方式(CompletionHandler)來獲取。

     在上面優化的框架中也有兩種機制的實現,第二種就直接將方法注冊到key的attachment中。第一種的實現如下圖:

    8

    就是通過會話碼及對象的Wait和Notify來實現的。
     
     下圖就是AIO的結構圖:

    9

      其實AIO是在NIO的幾個版本結構更新以后增加了更好的異步回調封裝,異步回調封裝就不多說了,這里就談談NIO迭代的幾個版本中新增加的幾個角色。

     ChannelFacade其實就和優化過程中使用長連接最通用的輸入輸出緩存設計一樣,對Channel外部在作了一層封裝,提供優化和攔截處理的入口。InputHandler也就是負責對于輸入內容的一個邏輯分析,判斷是否需要Flush,這和優化中的批量Flush也十分類似。

    后話
     總的看來其實NIO在客戶端的應用和我起初的想法還是比較一致,就是長連接模式加上事件驅動框架,同時如何處理好多線程并發控制及任務切分才是體現出NIO信道復用的關鍵,否則還是簡單的用客戶端連接池來的省心。

    posted on 2009-09-24 08:57 岑文初 閱讀(3374) 評論(7)  編輯  收藏

    評論

    # re: 客戶端NIO實踐分析[未登錄] 2009-09-24 10:00 dennis
    aio不僅僅是異步回調的封裝,aio在大量連接的情況下能有很高的效率,不過這對客戶端通常來說沒多大意義。非阻塞IO也是可以模擬aio的。  回復  更多評論
      

    # re: 客戶端NIO實踐分析 2009-10-06 22:01 xingbo.xu
    羨慕 有 這樣的 機會 實踐啊!

    像我們這樣的小公司,感覺做技術沒有前途;

    你給他簡單 把ibatis封裝哈 人家還不愛用!

    看來需要轉行!  回復  更多評論
      

    # re: 客戶端NIO實踐分析 2010-06-24 19:53 david.hew
    寫的很好,但是圖片好像看不到  回復  更多評論
      

    # re: 客戶端NIO實踐分析 2010-07-01 13:57 jaedong
    大蝦,你的圖圖還是看不到,繼續郁悶..  回復  更多評論
      

    # re: 客戶端NIO實踐分析 2011-02-17 15:55 游客
    圖還是看不到呀。  回復  更多評論
      

    # re: 客戶端NIO實踐分析[未登錄] 2011-03-02 14:33 littleJava
    么有圖片啊?!  回復  更多評論
      

    # re: 客戶端NIO實踐分析 2011-03-05 17:20 國畫
    羨慕有這樣的機會實踐啊!圖還是看不到呀  回復  更多評論
      


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 精品国产污污免费网站入口在线| 无码国模国产在线观看免费| 亚洲国产精品成人精品无码区 | 在线免费观看污网站| 337p日本欧洲亚洲大胆精品555588| 国产免费伦精品一区二区三区 | 国产在线一区二区综合免费视频| 亚洲色偷拍区另类无码专区| 综合偷自拍亚洲乱中文字幕 | 久久久久精品国产亚洲AV无码| 亚洲成人在线网站| 国产精品hd免费观看| 成人在线免费看片| 亚洲一区二区中文| 久久免费福利视频| 国产A在亚洲线播放| 三上悠亚在线观看免费| 毛茸茸bbw亚洲人| 国产精品一区二区三区免费 | 曰批全过程免费视频免费看| 国产成人青青热久免费精品| 亚洲gay片在线gv网站| 毛片免费在线播放| 久久精品亚洲一区二区| 99久久精品毛片免费播放| 国内精品99亚洲免费高清| 亚洲精品久久无码| 精品少妇人妻AV免费久久洗澡| 亚洲熟妇av午夜无码不卡| 无码视频免费一区二三区| 亚洲性无码一区二区三区| 午夜私人影院免费体验区| 亚洲精品动漫免费二区| 国产网站在线免费观看| 国产亚洲精品欧洲在线观看| 免费人妻无码不卡中文字幕18禁| 色费女人18女人毛片免费视频 | 4480yy私人影院亚洲| 亚洲黄色免费网站| ass亚洲**毛茸茸pics| 免费国产黄线在线观看|