1. 程式人生 > >Netty 學習筆記

Netty 學習筆記

            本文主要記錄學習Netty 的過程中的各種問題以及自己的心得記錄

一、先總體認識一下Netty 用到的元件以及在整個架構中是如何協調工作的,必不可少的元件包括:

BootStrap/ServerBootStrap       分別對應客戶端以及伺服器端,用來配置整個Netty 程式,串聯各個元件,一個Netty 程式也是由這部分開啟

Handler 為了支援各種協議和處理資料的方式,支援協議主要說的是解析bytebuf到可以解析的說句,handler主要用來處理各種事件,包括連線,資料接收,異常或者資料裝換等,在Netty 中,通訊的雙方建立連線後,會將資料按照ByteBuf的方式進行傳輸,也就是說傳輸都是位元組,用的抽象形式就是ByteBuf,例如http協議中,服務端就是通過Netty 提供的HttpRequestDecoder對輸入的ByteBuf位元組資料流進行處理,轉換為http物件,同時在輸出的時候,通過HttpResponseEncoder將輸出的http物件編碼成ByteBuf位元組資料流輸出,同理客戶端使用的是HttpRequestEncoder以及HttpResponseDecoder

EventLoop的目的是用來為channel處理IO操作,採用事件通知機制,一個EventLoop可以為多個Channel服務, EventLoopGoop中包括多個EventLoop,Channel就是一個Socket連線,或者其它和IO操作相關的元件

Future,在Netty 中所有的IO操作都是非同步的,因此,在呼叫方法後不能立刻得到正確或者錯誤的返回,但是可以通過執行sync方法,等待到方法的返回,或者直接註冊監聽,通過回撥的方式,Netty 會在操作執行完成後自動觸發回撥,所有的操作都會返回一個ChannelFuture

Netty 是一個非阻塞的,事件驅動的,網路程式設計框架,Netty 也會用執行緒來處理IO事件,一個Channel對應一個EventLoop,一個EventLoop會對應多個Channel,一個EventLoop對應一個執行緒,也就是一個執行緒可以處理多個Channel ,這也就是JAVA NIO的核心設計



如上圖,很形象的表達了Channel是如何註冊到EventLoop的,當一個連線到達,Netty 會註冊一個channel,這個channel就表示客戶端和服務端的連線,Netty 會使用EventLoopGroup(bossGroup)從另外一個EventLoopGroup(workerGroup)中分配一個EventLoop來繫結到channel,當然可以看到是迴圈從workerGroup中取出來EventLoop,所以這就是為什麼一個EventLoop會對應多個Channel,在這個channel的整個生命週期過程中,都會由這個EventLoop來為它服務,而這個EventLoop實際上就是一個無線迴圈的事件驅動執行緒

EventLoop實際上繼承了ScheduledExecutorService介面,在java的concurrent包裡面是提供了ScheduledExecutorService介面的實現,而Netty 框架同樣實現了ScheduledExecutorService介面,但是是內部程式碼實現了介面,同樣提供定時任務的執行,所以說EventLoop本身是支援定時任務的執行,不是依靠的jdk的實現(不是注入jdk的ScheduledExecutorService的實現),而是自己編寫的實現,通過ScheduledExecutorService介面能明確該類具有定時任務的功能

客戶端的Bootstrap 一般用一個EventLoopGroup, 而服務端的ServerBootStrap會用到兩個,第一個EventLoopGroup專門負責分配eventLoop給channel,負責兩者關係的繫結,而第二個EventLoopGroup用來處理channel後續的整個生命週期的通訊過程。(真正處理的是EventLoopGroup中的eventLoop),如果服務端僅由一個EventLoopGroup來處理所有請求和連線,在併發量很大的情況下,這個EventLoopGroup有可能會忙於處理已經接收到的連線而不能及時處理新的連線請求,如果使用兩個EventLoopGroup,使用專門的執行緒來處理連線過程,不會導致請求超時的情況,大大提高了併發處理能力,由於eventLoop的事件通知機制,減少了BIO模型下每個執行緒為單獨的socket的等待時間

在使用Netty 開發應用程式的時候,最重要的就是ChannelHandler,資料在一個ChannlePipleline,其中需要完成的編碼解碼,使用者操作等都是channelHandler,資料流都會經過每一個channlehandler並且被這個handler處理,ChannelHandler分為兩種ChannelOutboundHandler和ChannleInboundHandler,這兩個介面分別對應了兩個資料流向

如果資料是從外部流入應用程式,這是inbound

如果資料是從應用程式流到外部,這是outbound

一個channelpipeline可以將兩種handler混合在一起,資料寫進來的時候,傳到第一個inboundhandler,再繼續下一個inbound

資料寫出去的時候,從最後一個outhandler,再繼續從前一個outhandler

在channelpipeline中是混合了兩種handler,因為handler實現的介面不同,一個是處理入,一個是處理出,所以對於inbound event,Netty 會自動跳過outboundhandler,對於outbound event,Netty 會自動跳過inboundhandler

三種最常用的ChannelHandler

Encoders和Decoders

因為在網路傳輸時只能傳輸位元組流,因為,在傳送資料之前,必須將應用程式內部的message轉換為bytes,對應的,在接受到資料後,必須將接受到的bytes轉換成message,這個message可以是String,這樣就需要StringEncoder和StringDecoder,如果是Java物件,則需要編寫對應的encoder和decoder

decoder-解碼,將bytes解析稱為可以認識的物件,通過channelreader方法,在這個方法中呼叫具體的decode方法解碼傳遞過來的位元組流,再通過呼叫

channelHandlerContext.fireChannelRead方法將解碼後的物件傳遞給下一個handler

下一個handler就是應用程式最關心的邏輯handler了,真正的業務邏輯則是處理這個接受到的資料,Netty 提供一個最常用的基礎類SimpleChannelInboundHandler<T>,

其中這個泛型T就是這個業務邏輯Handler需要處理的資料型別(上一個解碼的handler已經處理好了),訊息達到這個handler後,Netty 框架會自動呼叫這個Handler的

channelRead0(ChannelHandlerContext, T)方法, T就是解碼後傳遞過來的資料,所以應用程式只需要關注如何在這個handler裡面編寫業務邏輯了。