資料結構1:
BOSS線程池:
EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1);
BOSS線程池中有一條線程工作,就是發現新的連接配接。 是以一個線程就夠了。
Netty是支援同時監聽多個端口的,是以BOSS線程池的大小按照需要監聽的伺服器端口數量進行設定就行了。 一般是1
工作線程池:
EventLoopGroup workLoogGroup = new NioEventLoopGroup(processorsNumber * 2, threadFactory, SelectorProvider.provider());
work線程池的大小,這個大小一般是實體機器/虛拟機器 可用核心的個數 * 2
work線程池中的線程(如果封裝的是JAVA NIO,那麼具體的線程實作類就是NioEventLoop)
負責 channel 事件監聽,并根據事件狀态,調用不同的事件就緒後的處理方法 ChannelHandler。
兩個線程池的設計 把nio 中selector的角色分成針對OP_ACCEPT 事件是BOSS線程來執行監聽的。
而讀寫事件是用work線程來輪詢監聽的。
是以說netty 封裝了一個線程池來處理 資料讀寫事件就緒 以及 真實的讀寫事件。
/指定Netty的Boss線程和work線程 和配置nio模型
serverBootstrap.group(bossLoopGroup, workLoogGroup);
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
serverBootstrap.bind(new InetSocketAddress("0.0.0.0", 83));
具體使用流程是:(基于nio模型)
1. BOSS線程負責發現連接配接到伺服器的新的channel(類比nio中SocketServerChannel的ACCEPT事件),并且将這個channel經過檢查後 注冊到WORK連接配接池 的某個線程中。
2. WORK線程 發現作業系統有一個它感興趣的IO事件就緒時(類比nio中SocketChannel的READ事件)則調用相應的ChannelHandler事件。當某個channel失效後(例如顯示調用ctx.close())這個channel将從綁定的EventLoop中被剔除。(nio中需要自己編寫方法,進行讀寫操作。)
3. 一個Work線程池的線程将按照底層nio的Selector的事件狀态,決定執行ChannelHandler中的哪一個事件方法 channelRegistered、channelUnregistered、channelActive、channelInactive。 重要的是channelRead channelReadComplete這兩個。
TCPServerHandler 繼承ChannelInboundHandlerAdapter 作為助手來提供以上方法。這個助手要注冊在每個channel中。
4. 執行完成後,work線程将一直輪詢 直到該work線程所管理的channel 發生了新的IO事件。
是以nio預設沒有提供線程池,真正的讀寫事件需要自己使用線程池來做。 而netty就預設提供線程池 worker線程。
資料結構2:
Channel:
Channel 其實就是每個socket連接配接的封裝。
每一個channel,都有獨立的handler、ChannelHandlerContext、ChannelPipeline、Attribute
每個ChannelPipeline 都可以添加多個handler。用來對該channel 做處理。
ChannelHandler:
handler這種叫法就是給使用者提供處理的地方。
在ChannelPipeline中,有若幹的過濾器。我們稱之為“ChannelHandler”(處理器或者過濾器)。
用于處理/過濾 流入資料的ChannelHandler,稱之為“ChannelInboundHandler”
用于處理/過濾 流出資料的ChannelHandler,稱之為“ChannelOutboundHandler”
資料在ChannelPipeline中有一個一個的Handler進行處理,并形成一個新的資料狀态
實作我們自己業務的“資料流入處理器:
我們可以實作ChannelInboundHandler接口或者ChannelOutboundHandler接口,
來實作我們自己業務的“資料流入處理器”或者“資料流出”處理器。
消息協定:
也采用ChannelHandler的形式通過ChannelPipeline 來添加。
具體有幾種:
HttpRequestDecoder/HttpResponseEncoder:
實作了Http協定的資料輸入格式的解析。這個類将資料編碼為HttpMessage對象,并交由下一個ChannelHandler進行處理。 反之将 伺服器端HttpReponse對象的描述轉換成ByteBuf對象形式
ByteArrayDecoder/ByteArrayDecoder :
最基礎的資料流輸入處理器,将所有的byte轉換為ByteBuf對象
(一般的實作類是:io.netty.buffer.UnpooledUnsafeDirectByteBuf)。我們進行一般的文本格式資訊傳輸到伺服器時,最好使用這個Handler将byte數組轉換為ByteBuf對象。
DelimiterBasedFrameDecoder:
這個資料流輸入處理器,會按照外部傳入的資料中給定的某個關鍵字元/關鍵字元串,重新将資料組裝為新的段落并發送給下一個Handler處理器。後文中,我們将使用這個處理器進行TCP半包的問題。
支援标準資料格式解析的處理器:
例如支援Google Protocol Buffers 資料格式解析的ProtobufDecoder和ProtobufVarint32FrameDecoder處理器。
還有支援标準的編碼成Google Protocol Buffers格式、JBoss Marshalling 格式、ZIP壓縮格式的ProtobufEncoder、ProtobufVarint32LengthFieldPrepender、MarshallingEncoder、JZlibEncoder等
Protobuf資料協定的內建:Netty利用自身的Channelpipeline的設計,對Protobuf資料協定進行了無縫結合。
Boss Marshalling資料協定的內建:JBoss Marshalling 是一個Java對象的序列化API包,修正了JDK自帶的序列化包的很多問題,又保持跟 java.io.Serializable 接口的相容。Netty通過封裝這個協定,可以幫助我們在用戶端-服務端簡便的進行對象系列化和反序列化。
HTTP Request / HTTP Response 協定的內建:在Netty中,可以友善的接受和發送Http協定。也就是說,我們可以使用Netty搭建自己的WEB伺服器,當然您還可以根據自己的業務要求,友善的設計類似于Struts、Spring MVC這樣的WEB架構。
nio 與 netty對比。
“技術層”架構本身隻對IO模型技術實作進行了封裝,并不關心IO模型中流淌的資料格式
“業務層”架構對資料格式也進行了處理,線程池進行了處理,讓我們可以抽出精力關注業務本身。