Netty-1

NIO深入理解

  1. 零拷貝
    • 在理解0拷貝之前 我們應(yīng)該先需要了解傳統(tǒng)IO的一個(gè)操作流程
      1. 傳統(tǒng)的io操作:首先需要進(jìn)行一個(gè) read操作 這里會(huì)發(fā)生一次用戶空間切換到內(nèi)核空間 內(nèi)核會(huì)采用DMA(直接內(nèi)存訪問(wèn)的方式)從磁盤讀取數(shù)據(jù)到內(nèi)核緩沖區(qū)
      2. 內(nèi)核緩沖區(qū)將數(shù)據(jù)拷貝到用戶空間 同時(shí)再次上下文切換到 用戶空間
      3. wirte 操作 也會(huì)發(fā)生一次上下文切換到內(nèi)核空間 同時(shí)將數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū)
      4. 內(nèi)核空間會(huì)將數(shù)據(jù)拷貝到 socket buffer 在由 socket buffer 寫入到協(xié)議引擎進(jìn)行數(shù)據(jù)的發(fā)送(這里有一個(gè)異步的過(guò)程 寫操作后 會(huì)切換到用戶空間)

      1. 用戶發(fā)送 sendfile 命令 進(jìn)行一次上下文切換到內(nèi)核空間 并采用DMA方式將數(shù)據(jù)拷貝到內(nèi)核緩沖區(qū)
      2. 這里內(nèi)核緩沖區(qū)的數(shù)據(jù)不會(huì)拷貝到 socket buffer而是將文件的描述信息放入如 內(nèi)存地址 以及文件長(zhǎng)度信息(異步 進(jìn)行以此上下文切換到用戶空間)
      3. 協(xié)議引擎會(huì)根據(jù) socketbuffer 中的文件描述信息 直接從內(nèi)核緩沖區(qū)將數(shù)據(jù)寫入 協(xié)議引擎發(fā)送出去
    • 什么是Reactor模式

      • 深入理解 Reactor模式對(duì)理解netty的設(shè)計(jì)模式有莫大的幫助担忧,所以在理解netty的設(shè)計(jì)模式時(shí)帖渠,我們需要理解Reactor他的一個(gè)設(shè)計(jì)理念:Reactor的五大角色構(gòu)成

        1. Handle (句柄或是描述符)
        • 即操作系統(tǒng)中的句柄师痕,是操作系統(tǒng)對(duì)資源的一種抽象,可以是打開的文件暖眼、一個(gè)連接(Socket)、Timer等。在網(wǎng)絡(luò)編程中药磺,一般指Socket Handle趴泌,文件描述符(fd)舟舒。將這個(gè)Handle注冊(cè)到Synchronous Event Demultiplexer中,就可以它發(fā)生的事件嗜憔,如READ秃励、WRITE、CLOSE等事件吉捶;handle本身是事件產(chǎn)生的發(fā)源地
        1. Synchronous Event Demultiplexer
          • 同步事件多路分用器夺鲜,本質(zhì)上是系統(tǒng)調(diào)用。比如linux中的select呐舔、poll币励、epoll等。它會(huì)一直阻塞直在handle上珊拼,直到有事件發(fā)生時(shí)才會(huì)返回 (在java nio領(lǐng)域中對(duì)應(yīng)的組件是selector食呻,對(duì)應(yīng)的阻塞方法就是 select方法)
        2. Initiation Dispatcher
          • 初始分發(fā)器,它提供了注冊(cè)澎现、刪除與轉(zhuǎn)發(fā)event handler的方法仅胞。當(dāng)Synchronous Event Demultiplexer檢測(cè)到handle上有事件發(fā)生時(shí),便會(huì)通知initiation dispatcher調(diào)用特定的event handler的回調(diào)(handle_event())方法昔头。
        3. Event Handler
          • 事件處理器饼问,定義事件處理的回調(diào)方法:handle_event(),以供InitiationDispatcher回調(diào)使用揭斧。(Netty相對(duì)于java nio在事件處理器上進(jìn)行了升級(jí)莱革,它為我們開發(fā)者提供了大量的回調(diào)方法,供我們?cè)谔囟ǖ氖录a(chǎn)生時(shí)實(shí)現(xiàn)相應(yīng)的回調(diào)方法進(jìn)行業(yè)務(wù)邏輯的處理)
        4. Concrete Event Handler
          • 具體的事件處理器讹开,繼承自Event Handler盅视,在回調(diào)方法中會(huì)實(shí)現(xiàn)具體的業(yè)務(wù)邏輯
      • Reactor模式的流程

        1. 當(dāng)應(yīng)用向Initiation Dispatcher 注冊(cè)具體的事件處理器時(shí),應(yīng)用會(huì)標(biāo)識(shí)出該事件處理器希望Initiation Dispatcher在某個(gè)事件發(fā)生時(shí)向其通知的該事件旦万,該事件與handle關(guān)聯(lián)
        2. Initiation Dispatcher會(huì)要求每個(gè)事件處理器向其傳遞內(nèi)部handle闹击。該handle向操作系統(tǒng)標(biāo)識(shí)了事件處理器
        3. 當(dāng)所有的事件處理器注冊(cè)完畢后,應(yīng)用會(huì)調(diào)用handle_events方法來(lái)啟動(dòng)Initiation Dispatcher的事件循環(huán)成艘。這時(shí)赏半,Initiation Dispatcher會(huì)將每個(gè)注冊(cè)的事件管理器的handle合并起來(lái),并使用同步事件分離器等待這些事件的發(fā)生淆两。比如說(shuō):TCP協(xié)議層會(huì)使用select同步事件分離器操作來(lái)等待客戶端發(fā)送數(shù)據(jù)到達(dá)連接的socket handle上
        4. 當(dāng)與某個(gè)事件源對(duì)應(yīng)的Handle變?yōu)閞eady狀態(tài)時(shí)(比如:TCP socket變?yōu)榈却x狀態(tài)時(shí))断箫。同步事件分離器就會(huì)通知Initiation Dispatcher
        5. Initiation Dispatcher會(huì)觸發(fā)事件處理器的回調(diào)方法,從而響應(yīng)這個(gè)處于ready狀態(tài)的Handle秋冰。當(dāng)事件發(fā)生時(shí)仲义,Initiation Dispatcher會(huì)將被事件源激活的Handle作為【key】來(lái)尋找并分發(fā)恰當(dāng)?shù)氖录幚砥鞯幕卣{(diào)方法。
        6. Initiation Dispatcher會(huì)回調(diào)事件處理器的handle_events回調(diào)方法來(lái)執(zhí)行特定與應(yīng)用的功能(開發(fā)者自己編寫功能)從而響應(yīng)這個(gè)事件。所發(fā)生的事件類型可以作為該方法的參數(shù)并被該方法內(nèi)部使用來(lái)執(zhí)行額外的特定于服務(wù)的分離與分發(fā)埃撵。
      • Netty 核心的幾個(gè)概念

        1. 一個(gè)EventLoopGroup當(dāng)中包含一個(gè)或多個(gè)EventLoop
        2. 一個(gè)EventLoop在它的整個(gè)生命周期當(dāng)中都只會(huì)與唯一一個(gè)Thread進(jìn)行綁定
        3. 所有由EventLoop所處理的各種I/O事件都將在它所關(guān)聯(lián)的那個(gè)Thread上進(jìn)行處理
        4. 一個(gè)Channel在它的整個(gè)生命周期中只會(huì)注冊(cè)在一個(gè)EventLoop上
        5. 一個(gè)EventLoop在運(yùn)行過(guò)程當(dāng)中赵颅,會(huì)被分配到多個(gè)Channel
      • 結(jié)論:

        1. 在Netty中,Channel的實(shí)現(xiàn)一定是線程安全的暂刘,基于此饺谬,我們可以存儲(chǔ)一個(gè)Channel的引用,并且在需要向遠(yuǎn)端發(fā)送數(shù)據(jù)時(shí)鸳惯,通過(guò)整個(gè)引用來(lái)調(diào)用Channel的相應(yīng)方法商蕴;即便當(dāng)時(shí)有很多線程都在使用它也不會(huì)出現(xiàn)線程問(wèn)題;而且消息一定會(huì)按照順序發(fā)送出去芝发。
        2. 我們?cè)跇I(yè)務(wù)開發(fā)中绪商,不要將長(zhǎng)時(shí)間執(zhí)行的耗時(shí)任務(wù)放入到EventLoop的執(zhí)行隊(duì)列中,因?yàn)樗鼘?huì)一直阻塞該線程所對(duì)應(yīng)的所有Channel上的其他執(zhí)行任務(wù)辅鲸,如果我們需要進(jìn)行阻塞調(diào)用或是耗時(shí)的操作格郁,那么我們就需要使用一個(gè)專門的EventExecutor(業(yè)務(wù)線程池)
        3. JDK所提供的Future只能通過(guò)手工的方式檢查執(zhí)行結(jié)果,而這個(gè)操作是會(huì)阻塞的独悴;Netty則對(duì)ChannelFuture進(jìn)行了增強(qiáng)例书,通過(guò)ChannelFutureListener以回調(diào)的方式獲取結(jié)果,去除了手工檢查的操作(觀察者模式);值得注意的是:ChannelFutureListener的operationComplete方法是由I/O線程執(zhí)行的刻炒,因此要注意的是不要在這里執(zhí)行耗時(shí)的操作决采,否則需要通過(guò)另外的線程或線程池來(lái)執(zhí)行。
        4. 在Netty中有兩種發(fā)送消息的方式坟奥,可以直接寫到Channel中树瞭,也可以寫到ChannelHandler所關(guān)聯(lián)的那個(gè)ChannelHandlerContext中。對(duì)于前一種方式來(lái)說(shuō)爱谁,消息會(huì)從ChannelPipline的末尾開始流動(dòng)晒喷;對(duì)于后一種方式來(lái)說(shuō),消息將從ChannelPipline中的下一個(gè)ChannelHandler開始流動(dòng)访敌。
        5. ChannelHandlerContext與ChannelHandler之間的關(guān)聯(lián)綁定關(guān)系是永遠(yuǎn)都不會(huì)發(fā)生改變的凉敲,因此對(duì)其進(jìn)行緩存是沒有任何問(wèn)題的。
        6. 對(duì)于Channel的同名方法來(lái)說(shuō)寺旺,ChannelHandlerContext的方法將會(huì)產(chǎn)生更短的事件流爷抓,所以我們應(yīng)該在可能的情況下利用這個(gè)特性來(lái)提升應(yīng)用的性能。
        7. 在實(shí)際的開發(fā)中我們經(jīng)常會(huì)遇到一個(gè)服務(wù)端可能會(huì)去要調(diào)用另外一個(gè)客戶端阻塑,這時(shí)這個(gè)服務(wù)端的角色就相當(dāng)于即作為服務(wù)端也作為客戶端蓝撇。這時(shí)我們需要注意在我們作為客戶端時(shí)我們應(yīng)該將對(duì)服務(wù)端和客戶端的channel綁定在同一個(gè)eventLoop上;
      • Netty 提供的三種緩沖區(qū)類型

        1. heap buffer 堆緩沖區(qū)
          • 優(yōu)點(diǎn):由于數(shù)據(jù)是存儲(chǔ)在JVM的堆中叮姑,因此可以快速的創(chuàng)建于快速的釋放唉地,并且他提供了直接訪問(wèn)內(nèi)部字節(jié)數(shù)組的方法
          • 缺點(diǎn):每次的讀寫操作,都需要先將數(shù)據(jù)復(fù)制到直接緩沖區(qū)中在進(jìn)行網(wǎng)絡(luò)傳輸
        2. direct buffer 直接緩沖區(qū)(在堆之外直接分配內(nèi)存空間传透,直接緩沖區(qū)不會(huì)占用堆的容量空間耘沼,因?yàn)樗怯刹僮飨到y(tǒng)在本地內(nèi)存進(jìn)行的數(shù)據(jù)分配)
          • 優(yōu)點(diǎn):在使用Sockte進(jìn)行數(shù)據(jù)傳遞時(shí),性能非常好朱盐,因?yàn)閿?shù)據(jù)直接位于操作系統(tǒng)的本地內(nèi)存中群嗤,所以不需要從JVM將數(shù)據(jù)復(fù)制到直接緩沖區(qū),性能很好兵琳。
          • 缺點(diǎn):因?yàn)镈irect Buffer是直接在操作系統(tǒng)內(nèi)存中狂秘,所以內(nèi)存空間的分配與釋放要比堆空間更加復(fù)雜,而且速度慢一些躯肌。(Netty通過(guò)提供內(nèi)存池來(lái)解決這個(gè)問(wèn)題)
          • 注意:直接緩沖區(qū)不支持通過(guò)字節(jié)數(shù)組的方式來(lái)直接訪問(wèn)數(shù)據(jù)(對(duì)于后端的業(yè)務(wù)消息的編解碼來(lái)說(shuō)者春,推薦使用HeapByteBuf;對(duì)于I/O通信線程在讀寫緩沖區(qū)時(shí)清女,推薦使用DirectByteBuf)
        3. composite buffer 復(fù)合緩沖區(qū)
      • JDK的ByteBuffer和Netty的ByteBuf的差異比對(duì)

        1. Netty的ByteBuf采用讀寫索引分離的策略(readerIndex與writerIndex)钱烟,一個(gè)初始化(里面尚未有任何數(shù)據(jù))的ByteBuf的readerIndex與witerIndex值都為0
        2. 當(dāng)讀索引和寫索引處于同一個(gè)位置時(shí),如果我們繼續(xù)讀取嫡丙,那么就會(huì)拋出IndexOutofBoundsException
        3. 對(duì)于ByteBuf的任何讀寫操作都會(huì)分別單獨(dú)維護(hù)讀索引與寫索引拴袭。maxCapacity最大的容量默認(rèn)是 Integer.MAX_VALUE。

代碼實(shí)例:

    public static void main(String[] args) {
      // 創(chuàng)建一個(gè)長(zhǎng)度為10 的Bytebuf
      ByteBuf byteBuf = Unpooled.buffer(10);

      for (int i=0;i<10;i++) {
          byteBuf.writeByte(i);
      }

      for ( int i=0; i<byteBuf.capacity();i++){
          System.out.println(byteBuf.getByte(i));
      }
  }

  public static void main(String[] args) {
      ByteBuf byteBuf = Unpooled.copiedBuffer("hello你 world", Charset.forName("utf-8"));

      if( byteBuf.hasArray()){
          // hasArray 判斷這個(gè)byteBuf背后真正的支持是不是一個(gè)字節(jié)數(shù)組 如果是 表示這是一個(gè)堆上的緩沖
          // 獲取byteBuf背后的真正數(shù)據(jù)載體
          byte[] array = byteBuf.array();
          System.out.println(new String(array,Charset.forName("utf-8")));
          System.out.println(byteBuf);

          System.out.println(byteBuf.arrayOffset());
          System.out.println(byteBuf.readerIndex());
          System.out.println(byteBuf.writerIndex());
          System.out.println(byteBuf.capacity());
          System.out.println(byteBuf.readableBytes());

          while (byteBuf.isReadable()){
              System.out.println((char) byteBuf.readByte());
          }
      }
  }

 public static void main(String[] args) {
      // 創(chuàng)建一個(gè) CompositeBuffer 復(fù)合緩沖區(qū)
      CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer();

      ByteBuf heapBuf = Unpooled.buffer(10);
      ByteBuf directBuf = Unpooled.directBuffer(8);
      // 將堆緩沖 和直接緩沖添加到復(fù)合緩沖區(qū)中
      compositeByteBuf.addComponents(heapBuf,directBuf);
      //compositeByteBuf.removeComponent(0);
      Iterator<ByteBuf> iterator = compositeByteBuf.iterator();
      while (iterator.hasNext()){
          System.out.println(iterator.next());
      }

      compositeByteBuf.forEach(System.out::println);
  }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末曙博,一起剝皮案震驚了整個(gè)濱河市拥刻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌父泳,老刑警劉巖般哼,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異尘吗,居然都是意外死亡逝她,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門睬捶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)黔宛,“玉大人,你說(shuō)我怎么就攤上這事擒贸⊥位危” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵介劫,是天一觀的道長(zhǎng)徽惋。 經(jīng)常有香客問(wèn)我,道長(zhǎng)座韵,這世上最難降的妖魔是什么险绘? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任踢京,我火速辦了婚禮,結(jié)果婚禮上宦棺,老公的妹妹穿的比我還像新娘瓣距。我一直安慰自己,他們只是感情好代咸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布蹈丸。 她就那樣靜靜地躺著,像睡著了一般呐芥。 火紅的嫁衣襯著肌膚如雪逻杖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天思瘟,我揣著相機(jī)與錄音荸百,去河邊找鬼。 笑死滨攻,一個(gè)胖子當(dāng)著我的面吹牛管搪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铡买,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼更鲁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了奇钞?” 一聲冷哼從身側(cè)響起澡为,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎景埃,沒想到半個(gè)月后媒至,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谷徙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年拒啰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片完慧。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谋旦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出屈尼,到底是詐尸還是另有隱情册着,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布脾歧,位于F島的核電站甲捏,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鞭执。R本人自食惡果不足惜司顿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一芒粹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧大溜,春花似錦是辕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)旁蔼。三九已至锨苏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間棺聊,已是汗流浹背伞租。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留限佩,地道東北人葵诈。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像祟同,于是被迫代替她去往敵國(guó)和親作喘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容