Netty系列(1)---基礎(chǔ)知識(shí)

https://www.cnblogs.com/imstudy/p/9908791.html (核心參考)
https://www.bilibili.com/video/av59683486/?p=4 (核心參考)

1.基本概念

#摘自官網(wǎng)
Netty is an asynchronous event-driven network application framework 
for rapid development of maintainable high performance protocol servers & clients. 
Netty .png

1.1 BIO/NIO/AIO

http://www.itdecent.cn/p/f34bb3169443 (參考此文)

1.1 Reactor 線程模型

無論是C++還是Java編寫的網(wǎng)絡(luò)框架,大多數(shù)都是基于Reactor模式進(jìn)行設(shè)計(jì)和開發(fā),
Reactor模式基于事件驅(qū)動(dòng),特別適合處理海量的I/O事件。

#Reactor 模型
是指通過一個(gè)或多個(gè)輸入同時(shí)傳遞給服務(wù)器的服務(wù)請(qǐng)求的事件驅(qū)動(dòng)處理模式。
服務(wù)端程序處理傳入多路請(qǐng)求,并將它們同步分派給請(qǐng)求對(duì)應(yīng)的處理線程,
Reactor 模式也叫 Dispatcher 模式,即 I/O 多了復(fù)用統(tǒng)一監(jiān)聽事件,
收到事件后分發(fā)(Dispatch 給某進(jìn)程),是編寫高性能網(wǎng)絡(luò)服務(wù)器的必備技術(shù)之一。

#Reactor 模型中有 2 個(gè)關(guān)鍵組成
1)Reactor:
Reactor 在一個(gè)單獨(dú)的線程中運(yùn)行,負(fù)責(zé)監(jiān)聽和分發(fā)事件,分發(fā)給適當(dāng)?shù)奶幚沓绦騺韺?duì) IO 事件做出反應(yīng)。
它就像公司的電話接線員,它接聽來自客戶的電話并將線路轉(zhuǎn)移到適當(dāng)?shù)穆?lián)系人。
2)Handlers:
處理程序執(zhí)行 I/O 事件要完成的實(shí)際事件,類似于客戶想要與之交談的公司中的實(shí)際官員。
Reactor 通過調(diào)度適當(dāng)?shù)奶幚沓绦騺眄憫?yīng) I/O 事件,處理程序執(zhí)行非阻塞操作。

#取決于 Reactor 的數(shù)量和 Hanndler 線程數(shù)量的不同,Reactor 模型有 3 個(gè)變種:
1)單 Reactor 單線程;
2)單 Reactor 多線程;
3)主從 Reactor 多線程。

可以這樣理解,Reactor 就是一個(gè)執(zhí)行 
while (true) { selector.select(); …} 循環(huán)的線程,
會(huì)源源不斷的產(chǎn)生新的事件,稱作反應(yīng)堆很貼切。

1.1.1 Reactor單線程模型

Reactor單線程模型,指的是所有的IO操作都在同一個(gè)NIO線程上面完成,
在這個(gè)單線程中要負(fù)責(zé)接收請(qǐng)求,處理IO,編解碼所有操作,
相當(dāng)于一個(gè)飯館只有一個(gè)人,同時(shí)負(fù)責(zé)前臺(tái)和后臺(tái)服務(wù),效率低。
Reactor單線程模型的這個(gè)NIO線程的職責(zé)如下:
1)作為NIO服務(wù)端,接收客戶端的TCP連接;
2)作為NIO客戶端,向服務(wù)端發(fā)起TCP連接;
3)讀取通信對(duì)端的請(qǐng)求或者應(yīng)答消息;
4)向通信對(duì)端發(fā)送消息請(qǐng)求或者應(yīng)答消息。

#server 端示例:
ServerBootstrap server = new ServerBootstrap();
// 1.當(dāng) worker 不存在, server.group(boss, boss) 是 Reactor 的單線程模型
NioEventLoopGroup reactorGroup = new NioEventLoopGroup(new DefaultThreadFactory("reactorGroup", true));
server.group(reactorGroup, reactorGroup) ...
Reactor單線程模型.png

1.1.2 多線程模型

多線程的優(yōu)點(diǎn)在于有"單獨(dú)的一個(gè)線程去處理請(qǐng)求","另外有一個(gè)線程池創(chuàng)建多個(gè)NIO線程去處理IO"。
相當(dāng)于一個(gè)飯館有一個(gè)前臺(tái)負(fù)責(zé)接待,有很多服務(wù)員去做后面的工作。

#Reactor多線程模型的特點(diǎn):
1)有專門一個(gè)NIO線程-Acceptor線程用于監(jiān)聽服務(wù)端,接收客戶端的TCP連接請(qǐng)求;
2)網(wǎng)絡(luò)IO操作-讀、寫等由一個(gè)NIO線程池負(fù)責(zé),線程池可以采用標(biāo)準(zhǔn)的JDK線程池實(shí)現(xiàn),
它包含一個(gè)任務(wù)隊(duì)列和N個(gè)可用的線程,由這些NIO線程負(fù)責(zé)消息的讀取、解碼、編碼和發(fā)送;
3)1個(gè)NIO線程可以同時(shí)處理N條鏈路,但是1個(gè)鏈路只對(duì)應(yīng)1個(gè)NIO線程,防止發(fā)生并發(fā)操作問題。

#server 端示例:
ServerBootstrap server = new ServerBootstrap();
// 2.當(dāng) worker 存在, boss 的 nThreads == 1 時(shí),  server.group(boss, worker) 是 Reactor 的多線程模型
NioEventLoopGroup acceptorGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("bossGroupExecutor", true));
NioEventLoopGroup ioGroup = new NioEventLoopGroup(new DefaultThreadFactory("bossGroupExecutor", true));
server.group(acceptorGroup, ioGroup) ...

在絕大多數(shù)場(chǎng)景下,Reactor多線程模型都可以滿足性能需求;
但是,在極個(gè)別特殊場(chǎng)景中,一個(gè)NIO線程負(fù)責(zé)監(jiān)聽和處理所有的客戶端連接可能會(huì)存在性能問題。
例如并發(fā)百萬客戶端連接,或者服務(wù)端需要對(duì)客戶端握手進(jìn)行安全認(rèn)證,但是認(rèn)證本身非常損耗性能。
Reactor多線程模型.png

1.1.3 Reactor主從線程模型

多線程模型的缺點(diǎn)在于并發(fā)量很高的情況下,只有一個(gè)Reactor單線程去處理是來不及的,
就像飯館只有一個(gè)前臺(tái)接待很多客人也是不夠的。為此需要使用主從線程模型。
>> 主從線程模型:"一組線程池接收請(qǐng)求","一組線程池處理IO"。

#主從Reactor線程模型的特點(diǎn)是:
1)MainReactor(NIO線程池1)負(fù)責(zé)客戶端的連接請(qǐng)求,并將請(qǐng)求轉(zhuǎn)交給 SubReactor;
2)SubReactor(NIO線程池2)負(fù)責(zé)相應(yīng)通道的 IO 讀寫請(qǐng)求;
3)非 IO 請(qǐng)求(具體邏輯處理)的任務(wù)則會(huì)直接寫入隊(duì)列,等待 worker threads 進(jìn)行處理。

#server 端示例:
ServerBootstrap server = new ServerBootstrap();
// 3.當(dāng) worker 存在, boss 的 nThreads > 1 時(shí),  server.group(boss, worker) 是 Reactor 的主從線程模型
NioEventLoopGroup acceptorGroup = new NioEventLoopGroup(new DefaultThreadFactory("bossGroupExecutor", true));
NioEventLoopGroup ioGroup = new NioEventLoopGroup(new DefaultThreadFactory("bossGroupExecutor", true));
server.group(acceptorGroup, ioGroup) ...
Reactor主從線程模型.png

2.Netty中的相關(guān)組件

2.1 NioEventLoop

2.1.1 Bootstrap、ServerBootstrap

Bootstrap 意思是引導(dǎo),一個(gè) Netty 應(yīng)用通常由一個(gè) Bootstrap 開始,
主要作用是配置整個(gè) Netty 程序,串聯(lián)各個(gè)組件,Netty 中:
Bootstrap 類是客戶端程序的啟動(dòng)引導(dǎo)類,ServerBootstrap 是服務(wù)端啟動(dòng)引導(dǎo)類。

2.1.2 Selector

Netty 基于 Selector 對(duì)象實(shí)現(xiàn) I/O 多路復(fù)用,通過Selector, 一個(gè)線程可以監(jiān)聽多個(gè)連接的 Channel 事件。

當(dāng)向一個(gè) Selector 中注冊(cè) Channel 后,Selector 內(nèi)部的機(jī)制就可以
自動(dòng)不斷地查詢(Select) 這些注冊(cè)的 Channel 是否有已就緒的 I/O 事件(例如可讀,可寫,網(wǎng)絡(luò)連接完成等),
這樣程序就可以很簡(jiǎn)單地使用一個(gè)線程高效地管理多個(gè) Channel 。

2.1.3 NioEventLoop & NioEventLoopGroup

#NioEventLoop 
主要方法是run(),是整個(gè)Netty執(zhí)行過程的邏輯代碼實(shí)現(xiàn)。
NioEventLoop中維護(hù)了一個(gè)線程和任務(wù)隊(duì)列,支持異步提交執(zhí)行任務(wù),
線程啟動(dòng)時(shí)會(huì)調(diào)用 NioEventLoop 的 run 方法,執(zhí)行 I/O 任務(wù)和非 I/O 任務(wù):
>> I/O 任務(wù),即 selectionKey 中 ready 的事件,如
accept、connect、read、write 等,由 processSelectedKeys 方法觸發(fā)。
>> 非 IO 任務(wù),添加到 taskQueue 中的任務(wù),如 register0、bind0 等任務(wù),由 runAllTasks 方法觸發(fā)。
兩種任務(wù)的執(zhí)行時(shí)間比由變量 ioRatio 控制,默認(rèn)為 50,
則表示允許非 IO 任務(wù)執(zhí)行的時(shí)間與 IO 任務(wù)的執(zhí)行時(shí)間相等。

Netty 的 IO 線程 NioEventLoop 由于聚合了多路復(fù)用器 Selector,可以同時(shí)并發(fā)處理成百上千個(gè)客戶端連接。
當(dāng)線程從某客戶端 Socket 通道進(jìn)行讀寫數(shù)據(jù)時(shí),若沒有數(shù)據(jù)可用時(shí),該線程可以進(jìn)行其他任務(wù)。
線程通常將非阻塞 IO 的空閑時(shí)間用于在其他通道上執(zhí)行 IO 操作,所以單獨(dú)的線程可以管理多個(gè)輸入和輸出通道。
由于讀寫操作都是非阻塞的,這就可以充分提升 IO 線程的運(yùn)行效率,避免由于頻繁 I/O 阻塞導(dǎo)致的線程掛起。
一個(gè) I/O 線程可以并發(fā)處理 N 個(gè)客戶端連接和讀寫操作這從根本上解決了傳統(tǒng)同步阻塞 I/O 一連接一線程模型,架構(gòu)的性能、彈性伸縮能力和可靠性都得到了極大的提升。

#NioEventLoopGroup
NioEventLoopGroup,主要管理 eventLoop 的生命周期,
可以理解為一個(gè)線程池,內(nèi)部維護(hù)了一組線程,
每個(gè)線程(NioEventLoop)負(fù)責(zé)處理多個(gè) Channel 上的事件,而一個(gè) Channel 只對(duì)應(yīng)于一個(gè)線程。
主要方法是newChild,EventLoop的工廠類。EventLoopGroup.newChild創(chuàng)建EventLoop對(duì)象。
OioEventLoopGroup除外,它沒有實(shí)現(xiàn)newChild方法,調(diào)用父類的并創(chuàng)建ThreadPerChannelEventLoop對(duì)象。

#EventExecutorGroup
線程池實(shí)現(xiàn),主要成員是children數(shù)組,主要方法是next(),獲得線程池中的一個(gè)線程,由子類調(diào)用。
由于Oio采用的是Thread per Channel機(jī)制,所以沒有實(shí)現(xiàn)前面兩個(gè)。

#EventExecutor
Task的執(zhí)行類,主要成員是taskQueue以及真正的運(yùn)行線程對(duì)象executor,
主要方法是taskQueue操作方法execute、takeTask、addTask等,以及doStartThread方法。

2.2 Channel, ChannelPipeline, ChannelHandlerContext

2.2.1 io.netty.channel.Channel

A nexus to a network socket or a component which is capable of I/O operations such as read, write, connect, and bind.
A channel provides a user:
 * <li>the current state of the channel (e.g. is it open? is it connected?),</li>
 * <li>the {@linkplain ChannelConfig configuration parameters} of the channel (e.g. receive buffer size),</li>
 * <li>the I/O operations that the channel supports (e.g. read, write, connect, and bind), and</li>
 * <li>the {@link ChannelPipeline} which handles all I/O events and requests associated with the channel.</li>

每個(gè) client 都會(huì)與 server 建立一個(gè)連接, 抽象為 channel.
#常見實(shí)現(xiàn)類
>> NioSocketChannel,異步的客戶端 TCP Socket 連接。
>> NioServerSocketChannel,異步的服務(wù)器端 TCP Socket 連接。
>> NioDatagramChannel,異步的 UDP 連接。
>> NioSctpChannel,異步的客戶端 Sctp 連接。
>> NioSctpServerChannel,異步的 Sctp 服務(wù)器端連接,這些通道涵蓋了 UDP 和 TCP 網(wǎng)絡(luò) IO 以及文件 IO。
>> OioSocketChannel, 同步的客戶端 TCP Socket 連接.
>> OioServerSocketChannel, 同步的服務(wù)器端 TCP Socket 連接.
>> OioDatagramChannel, 同步的 UDP 連接
>> OioSctpChannel, 同步的 Sctp 服務(wù)器端連接.
>> OioSctpServerChannel, 同步的客戶端 TCP Socket 連接.

2.2.2 ChannelPipeline(默認(rèn)實(shí)現(xiàn)是: DefaultChannelPipeline)

------------ ------------ ------------ ------------
ChannelPipeline 是一個(gè)與 Channel 所關(guān)聯(lián)的容器(一一對(duì)應(yīng))
其中存放的是一個(gè)一個(gè)的 ChannelHandlerContext 對(duì)象
而 ChannelHandlerContext 里面存放的是一個(gè)一個(gè)的 ChannelHandler 對(duì)象
------------ ------------ ------------ ------------
All I/O operations are asynchronous.
All I/O operations in Netty are asynchronous.  It means any I/O calls will return immediately with no guarantee that the requested I/O operation has been completed at the end of the call.  
Instead, you will be returned with a {@link ChannelFuture} instance which will notify you when the requested I/O operation has succeeded, failed, or canceled.

#1.用到了'高級(jí)攔截過濾器'模式
A list of {@link ChannelHandler}s which handles or intercepts inbound events and outbound operations of a {@link Channel}.  
{@link ChannelPipeline} implements an advanced form of the <a >Intercepting Filter</a> pattern to give a user full control over how an event is handled and how the {@link ChannelHandler}s in a pipeline interact with each other.

#2.ChannelPipeline創(chuàng)建過程
Each channel has its own pipeline and it is created automatically when a new channel is created.

#3.事件在ChannelPipeline中的流動(dòng)過程: 真正的處理是交給 ChannelHandler 來處理的
 * The following diagram describes how I/O events are processed by {@link ChannelHandler}s in a {@link ChannelPipeline}
 * typically. An I/O event is handled by either a {@link ChannelInboundHandler} or a {@link ChannelOutboundHandler}
 * and be forwarded to its closest handler by calling the event propagation methods defined in
 * {@link ChannelHandlerContext}, such as {@link ChannelHandlerContext#fireChannelRead(Object)} and
 * {@link ChannelHandlerContext#write(Object)}.
 *
 * <pre>
 *                                                 I/O Request
 *                                            via {@link Channel} or
 *                                        {@link ChannelHandlerContext}
 *                                                      |
 *  +---------------------------------------------------+---------------+
 *  |                           ChannelPipeline         |               |
 *  |                                                  \|/              |
 *  |    +---------------------+            +-----------+----------+    |
 *  |    | Inbound Handler  N  |            | Outbound Handler  1  |    |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |              /|\                                  |               |
 *  |               |                                  \|/              |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |    | Inbound Handler N-1 |            | Outbound Handler  2  |    |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |              /|\                                  .               |
 *  |               .                                   .               |
 *  | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
 *  |        [ method call]                       [method call]         |
 *  |               .                                   .               |
 *  |               .                                  \|/              |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |    | Inbound Handler  2  |            | Outbound Handler M-1 |    |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |              /|\                                  |               |
 *  |               |                                  \|/              |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |    | Inbound Handler  1  |            | Outbound Handler  M  |    |
 *  |    +----------+----------+            +-----------+----------+    |
 *  |              /|\                                  |               |
 *  +---------------+-----------------------------------+---------------+
 *                  |                                  \|/
 *  +---------------+-----------------------------------+---------------+
 *  |               |                                   |               |
 *  |       [ Socket.read() ]                    [ Socket.write() ]     |
 *  |                                                                   |
 *  |  Netty Internal I/O Threads (Transport Implementation)            |
 *  +-------------------------------------------------------------------+
 * </pre>

#4.如何將一個(gè)ChannelPipeline中的ChannelHandler傳遞給下一個(gè)ChannelHandler 
#(這里在自定義 ChannelHandler時(shí), 尤其重要!!!!!!!!!!!!!!!!)
 * As you might noticed in the diagram shows, a handler has to invoke the event propagation methods in
 * {@link ChannelHandlerContext} to forward an event to its next handler.  Those methods include:
 * <ul>
 * <li>Inbound event propagation methods:
 *     <ul>
 *     <li>{@link ChannelHandlerContext#fireChannelRegistered()}</li>
 *     <li>{@link ChannelHandlerContext#fireChannelActive()}</li>
 *     <li>{@link ChannelHandlerContext#fireChannelRead(Object)}</li>
 *     <li>{@link ChannelHandlerContext#fireChannelReadComplete()}</li>
 *     <li>{@link ChannelHandlerContext#fireExceptionCaught(Throwable)}</li>
 *     <li>{@link ChannelHandlerContext#fireUserEventTriggered(Object)}</li>
 *     <li>{@link ChannelHandlerContext#fireChannelWritabilityChanged()}</li>
 *     <li>{@link ChannelHandlerContext#fireChannelInactive()}</li>
 *     <li>{@link ChannelHandlerContext#fireChannelUnregistered()}</li>
 *     </ul>
 * </li>
 * <li>Outbound event propagation methods:
 *     <ul>
 *     <li>{@link ChannelHandlerContext#bind(SocketAddress, ChannelPromise)}</li>
 *     <li>{@link ChannelHandlerContext#connect(SocketAddress, SocketAddress, ChannelPromise)}</li>
 *     <li>{@link ChannelHandlerContext#write(Object, ChannelPromise)}</li>
 *     <li>{@link ChannelHandlerContext#flush()}</li>
 *     <li>{@link ChannelHandlerContext#read()}</li>
 *     <li>{@link ChannelHandlerContext#disconnect(ChannelPromise)}</li>
 *     <li>{@link ChannelHandlerContext#close(ChannelPromise)}</li>
 *     <li>{@link ChannelHandlerContext#deregister(ChannelPromise)}</li>
 *     </ul>
 * </li>
 * </ul>

#5.當(dāng)業(yè)務(wù)線程有耗時(shí)任務(wù)時(shí), 需自定義業(yè)務(wù)線程池來實(shí)現(xiàn)
 * static final {@link EventExecutorGroup} group = new {@link DefaultEventExecutorGroup}(16);
 * ...
 *
 * {@link ChannelPipeline} pipeline = ch.pipeline();
 *
 * pipeline.addLast("decoder", new MyProtocolDecoder());
 * pipeline.addLast("encoder", new MyProtocolEncoder());
 *
 * // Tell the pipeline to run MyBusinessLogicHandler's event handler methods
 * // in a different thread than an I/O thread so that the I/O thread is not blocked by
 * // a time-consuming task.
 * // If your business logic is fully asynchronous or finished very quickly, you don't
 * // need to specify a group.
 * pipeline.addLast(group, "handler", new MyBusinessLogicHandler());

#6.ChannelPipeline是線程安全的, 因此可動(dòng)態(tài)增刪 ChannelHandler
 * A {@link ChannelHandler} can be added or removed at any time because a {@link ChannelPipeline} is thread safe.
 * For example, you can insert an encryption handler when sensitive information is about to be exchanged, and remove it
 * after the exchange.

2.2.3 ChannelHandlerContext (默認(rèn)實(shí)現(xiàn)類: DefaultChannelHandlerContext)

#基本情形
底層是一個(gè)雙向鏈表, 是 ChannelHandler 與 ChannelPipeline 之間交互的橋梁和紐帶
其中幾個(gè)方法, 可以分別獲取到 Channel 對(duì)象/ChannelHandler 對(duì)象/ ChannelPipeline對(duì)象:
Channel channel();
ChannelHandler handler();
ChannelPipeline pipeline();
從而構(gòu)成了四大組件(前者包含后者):
Channel-->ChannelPipeline-->ChannelHandlerContext-->ChannelHandler

#事件在入站處理器之間傳遞的關(guān)鍵: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
自定義Handler時(shí), 請(qǐng)?jiān)u估是否需調(diào)用 firexxx 方法, 否則可能無法傳遞到下一個(gè) ChannelHandler

#事件的出站處理器之間傳遞的關(guān)鍵: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
自定義Handler時(shí), 請(qǐng)?jiān)u估是否需調(diào)用 write, read 等方法, 否則可能無法傳遞到下一個(gè) ChannelHandler

2.2.4 ChannelHandler (可類比成 filter)

ChannelHandler 是一個(gè)接口,處理 I/O 事件或攔截 I/O 操作,
并將其轉(zhuǎn)發(fā)到其 ChannelPipeline(業(yè)務(wù)處理鏈)中的下一個(gè)處理程序。

#主要分為兩大類
>> ChannelInboundHandler 用于處理入站 I/O 事件。
>> ChannelOutboundHandler 用于處理出站 I/O 操作。

#適配器類為
>> ChannelInboundHandlerAdapter 用于處理入站 I/O 事件。
>> ChannelOutboundHandlerAdapter 用于處理出站 I/O 操作。
>> ChannelDuplexHandler 用于處理入站和出站事件。

#對(duì)于 Inbound 事件:
>> Inbound 事件是通知事件, 當(dāng)某件事情已經(jīng)就緒后, 通知上層.
>> Inbound 事件發(fā)起者是 unsafe
>> Inbound 事件的處理者是 Channel, 如果用戶沒有實(shí)現(xiàn)自定義的處理方法, 
那么Inbound 事件默認(rèn)的處理者是 TailContext, 并且其處理方法是空實(shí)現(xiàn).
>> Inbound 事件在 Pipeline 中傳輸方向是 head -> tail
>> 在 ChannelHandler 中處理事件時(shí), 如果這個(gè) Handler 不是最后一個(gè) Hnalder, 
則需要調(diào)用 ctx.fireIN_EVT (例如 ctx.fireChannelActive) 將此事件繼續(xù)傳播下去. 
!!!!!!!!!!!!!!!!!!!如果不這樣做, 那么此事件的傳播會(huì)提前終止.
>> Inbound 事件流: Context.fireIN_EVT -> Connect.findContextInbound -> nextContext.invokeIN_EVT -> nextHandler.IN_EVT -> nextContext.fireIN_EVT

#對(duì)于 Outbound事件:
>> Outbound 事件是請(qǐng)求事件(由 Connect 發(fā)起一個(gè)請(qǐng)求, 并最終由 unsafe 處理這個(gè)請(qǐng)求)
>> Outbound 事件的發(fā)起者是 Channel
>> Outbound 事件的處理者是 unsafe
>> Outbound 事件在 Pipeline 中的傳輸方向是 tail -> head.
>> 在 ChannelHandler 中處理事件時(shí), 如果這個(gè) Handler 不是最后一個(gè) Hnalder, 
則需要調(diào)用 ctx.xxx (例如 ctx.connect) 將此事件繼續(xù)傳播下去. 
!!!!!!!!!!!!!!!!!!!如果不這樣做, 那么此事件的傳播會(huì)提前終止.
>> Outbound 事件流: Context.OUT_EVT -> Connect.findContextOutbound -> nextContext.invokeOUT_EVT -> nextHandler.OUT_EVT -> nextContext.OUT_EVT
Channel-ChannelPipeline-ChannelHandlerContext-ChannelHandler.png
#Channel-ChannelPipeline-ChannelHandlerContext-ChannelHandler
>> 一個(gè) Channel 包含了一個(gè) ChannelPipeline,
>> 一個(gè) ChannelPipeline 中又維護(hù)了一個(gè)由 ChannelHandlerContext 組成的雙向鏈表,
>> 一個(gè) ChannelHandlerContext 中又關(guān)聯(lián)著一個(gè) ChannelHandler。
>> 入站事件和出站事件在一個(gè)雙向鏈表中,
入站事件會(huì)從鏈表 head 往后傳遞到最后一個(gè)入站的 handler,
出站事件會(huì)從鏈表 tail 往前傳遞到最前一個(gè)出站的 handler,
兩種類型的 handler 互不干擾。

#以下述代碼為例
server.group(bossGroup, workerGroup)
    .channel(NioServerSocketChannel.class)
    .childHandler(new ChannelInitializer<NioSocketChannel>() {
        @Override
        protected void initChannel(NioSocketChannel ch) throws Exception {
            ch.pipeline().addLast(new StringDecoder())...
        }
    });

#ch.pipeline().addLast(new StringDecoder()) 構(gòu)建過程為:
--> DefaultChannelPipeline#addLast(String, ChannelHandler)
    --> DefaultChannelPipeline#addLast(EventExecutorGroup, String, ChannelHandler)
        #新建一個(gè) ChannelHandlerContext
        --> DefaultChannelPipeline#newContext
            #將 ChannelHandler 作為 ChannelHandlerContext 構(gòu)造器的一個(gè)入?yún)⒕S護(hù)
            --> DefaultChannelHandlerContext#DefaultChannelHandlerContext
        #維護(hù) ChannelHandlerContext 的鏈表的前后節(jié)點(diǎn)
        --> DefaultChannelPipeline#addLast0

2.2.5 ChannelOption

是用于通過ChannelConfig, 配置與Channel相關(guān)的, 網(wǎng)絡(luò)層相關(guān)的配置

1、ChannelOption.SO_BACKLOG
ChannelOption.SO_BACKLOG對(duì)應(yīng)的是tcp/ip協(xié)議listen函數(shù)中的backlog參數(shù),
函數(shù)listen(int socketfd,int backlog)用來初始化服務(wù)端可連接隊(duì)列,
服務(wù)端處理客戶端連接請(qǐng)求是順序處理的,所以同一時(shí)間只能處理一個(gè)客戶端連接,
多個(gè)客戶端來的時(shí)候,服務(wù)端將不能處理的客戶端連接請(qǐng)求放在隊(duì)列中等待處理,backlog參數(shù)指定了隊(duì)列的大小
服務(wù)端接受連接的隊(duì)列長(zhǎng)度,如果隊(duì)列已滿,客戶端連接將被拒絕。默認(rèn)值,Windows為200,其他為128。

2、ChannelOption.SO_REUSEADDR
ChanneOption.SO_REUSEADDR對(duì)應(yīng)于套接字選項(xiàng)中的SO_REUSEADDR,
Socket參數(shù),地址復(fù)用,默認(rèn)值False。有四種情況可以使用:
(1) 當(dāng)有一個(gè)有相同本地地址和端口的socket1處于TIME_WAIT狀態(tài)時(shí),
而你希望啟動(dòng)的程序的socket2要占用該地址和端口,比如重啟服務(wù)且保持先前端口。
(2) 有多塊網(wǎng)卡或用IP Alias技術(shù)的機(jī)器在同一端口啟動(dòng)多個(gè)進(jìn)程,但每個(gè)進(jìn)程綁定的本地IP地址不能相同。
(3) 單個(gè)進(jìn)程綁定相同的端口到多個(gè)socket上,但每個(gè)socket綁定的ip地址不同。
(4) 完全相同的地址和端口的重復(fù)綁定。但這只用于UDP的多播,不用于TCP。

3、ChannelOption.SO_KEEPALIVE
Channeloption.SO_KEEPALIVE參數(shù)對(duì)應(yīng)于套接字選項(xiàng)中的SO_KEEPALIVE,
該參數(shù)用于設(shè)置TCP連接,當(dāng)設(shè)置該選項(xiàng)以后,連接會(huì)測(cè)試鏈接的狀態(tài),這個(gè)選項(xiàng)用于可能長(zhǎng)時(shí)間沒有數(shù)據(jù)交流的連接。
當(dāng)設(shè)置該選項(xiàng)以后,如果在兩小時(shí)內(nèi)沒有數(shù)據(jù)的通信時(shí),TCP會(huì)自動(dòng)發(fā)送一個(gè)活動(dòng)探測(cè)數(shù)據(jù)報(bào)文。
Netty中一般關(guān)閉該選項(xiàng), 而是使用IdleHandler 來實(shí)現(xiàn).

4、ChannelOption.SO_SNDBUF和ChannelOption.SO_RCVBUF
>> ChannelOption.SO_SNDBUF參數(shù)對(duì)應(yīng)于套接字選項(xiàng)中的SO_SNDBUF,
發(fā)送緩沖區(qū)用于保存發(fā)送數(shù)據(jù),直到發(fā)送成功。
Socket參數(shù),TCP數(shù)據(jù)發(fā)送緩沖區(qū)大小。
該緩沖區(qū)即TCP發(fā)送滑動(dòng)窗口,linux操作系統(tǒng)可使用命令:
cat /proc/sys/net/ipv4/tcp_smem 查詢其大小。
>> ChannelOption.SO_RCVBUF參數(shù)對(duì)應(yīng)于套接字選項(xiàng)中的SO_RCVBUF
接收緩沖區(qū)用于保存網(wǎng)絡(luò)協(xié)議站內(nèi)收到的數(shù)據(jù),直到應(yīng)用程序讀取成功。

5、ChannelOption.SO_LINGER
ChannelOption.SO_LINGER參數(shù)對(duì)應(yīng)于套接字選項(xiàng)中的SO_LINGER,
Socket參數(shù),關(guān)閉Socket的延遲時(shí)間,默認(rèn)值為-1,表示禁用該功能。
>> -1表示socket.close()方法立即返回,但OS底層會(huì)將發(fā)送緩沖區(qū)全部發(fā)送到對(duì)端。
>> 0表示socket.close()方法立即返回,OS放棄發(fā)送緩沖區(qū)的數(shù)據(jù)直接向?qū)Χ税l(fā)送RST包,對(duì)端收到復(fù)位錯(cuò)誤。
>> 非0整數(shù)值表示調(diào)用socket.close()方法的線程被阻塞直到延遲時(shí)間到或發(fā)送緩沖區(qū)中的數(shù)據(jù)發(fā)送完畢,若超時(shí),則對(duì)端會(huì)收到復(fù)位錯(cuò)誤。

6、ChannelOption.TCP_NODELAY
ChannelOption.TCP_NODELAY參數(shù)對(duì)應(yīng)于套接字選項(xiàng)中的TCP_NODELAY,該參數(shù)的使用與Nagle算法有關(guān)。
Nagle算法是將小的數(shù)據(jù)包組裝為更大的幀然后進(jìn)行發(fā)送,而不是輸入一次發(fā)送一次,
因此在數(shù)據(jù)包不足的時(shí)候會(huì)等待其他數(shù)據(jù)的到了,組裝成大的數(shù)據(jù)包進(jìn)行發(fā)送,
雖然該方式有效提高網(wǎng)絡(luò)的有效負(fù)載,但是卻造成了延時(shí),

這個(gè)參數(shù),與是否開啟Nagle算法是反著來的,true表示關(guān)閉,false表示開啟。
通俗地說,如果要求高實(shí)時(shí)性,有數(shù)據(jù)發(fā)送時(shí)就馬上發(fā)送,就關(guān)閉,
如果需要減少發(fā)送次數(shù)減少網(wǎng)絡(luò)交互,就開啟。

與TCP_NODELAY相對(duì)應(yīng)的是TCP_CORK,該選項(xiàng)是需要等到發(fā)送的數(shù)據(jù)量最大的時(shí)候,一次性發(fā)送數(shù)據(jù),適用于文件傳輸。

7、ALLOW_HALF_CLOSURE
Netty參數(shù),一個(gè)連接的遠(yuǎn)端關(guān)閉時(shí)本地端是否關(guān)閉,默認(rèn)值為False。
>> 為False時(shí),連接自動(dòng)關(guān)閉;
>> 為True時(shí),觸發(fā)ChannelInboundHandler的userEventTriggered()方法,事件為ChannelInputShutdownEvent。

8、SO_BROADCAST
Socket參數(shù),設(shè)置廣播模式。

2.2.6 其他

#1.AttributeMap, AttributeKey, Attribute
比如一個(gè)流程中, 有4個(gè)Handler, 在第二個(gè)Handler中加入了業(yè)務(wù)數(shù)據(jù), 在第四個(gè)Handler中想要取出來進(jìn)行處理, 可以采用Attribute
主要維護(hù)業(yè)務(wù)運(yùn)行數(shù)據(jù), 在運(yùn)行時(shí)候, 向程序中動(dòng)態(tài)添加業(yè)務(wù)數(shù)據(jù)
ChannelHandlerContext 與 Channel 都繼承了 AttributeMap接口
ChannelHandlerContext.attr(..) == Channel.attr(..) // 4.1開始, 之前的版本不等
4.1版本后, 若想使得key不重復(fù), 可以在自己的 ChannelHandler 中定義 private static final AttributeKey

#2.ChannelConfig
class Channel {
    private ChannelConfig config;
    ....... attribute
}

2.3 Netty中的Future, ChannelFuture, ChannelPromise

#異步讀寫操作的架構(gòu)思想及觀察者模式的應(yīng)用
當(dāng)一個(gè)異步過程調(diào)用發(fā)出后,調(diào)用者不能立刻得到結(jié)果。
實(shí)際處理這個(gè)調(diào)用的部件在完成后,通過狀態(tài)、通知和回調(diào)來通知調(diào)用者。

Netty 中的 I/O 操作是異步的,包括 Bind、Write、Connect 等操作會(huì)簡(jiǎn)單的返回一個(gè) ChannelFuture。
調(diào)用者并不能立刻獲得結(jié)果,而是通過 Future-Listener 機(jī)制,
用戶可以方便的主動(dòng)獲取或者通過通知機(jī)制獲得 IO 操作結(jié)果。

當(dāng) Future 對(duì)象剛剛創(chuàng)建時(shí),處于非完成狀態(tài),
調(diào)用者可以通過返回的 ChannelFuture 來獲取操作執(zhí)行的狀態(tài),注冊(cè)監(jiān)聽函數(shù)來執(zhí)行完成后的操作。

3.Netty中的一些設(shè)計(jì)模式

#1.適配器模式
ChannelHandlerAdapter 
ChannelInboundHandler 
ChannelInboundHandlerAdapter 

#2.觀察者模式
Future, ChannelFuture, ChannelPromise

#3.模板模式
SimpleChannelInboundHandler (其有一abstract方法供子類實(shí)現(xiàn))

4.Netty的實(shí)用性

4.1 應(yīng)用領(lǐng)域

1)互聯(lián)網(wǎng)行業(yè):
在分布式系統(tǒng)中,各個(gè)節(jié)點(diǎn)之間需要遠(yuǎn)程服務(wù)調(diào)用,高性能的 RPC 框架必不可少,
Netty 作為異步高性能的通信框架,往往作為基礎(chǔ)通信組件被這些 RPC 框架使用。
如阿里分布式服務(wù)框架 Dubbo 的 RPC 框架使用 Dubbo 協(xié)議進(jìn)行節(jié)點(diǎn)間通信,
Dubbo 協(xié)議默認(rèn)使用 Netty 作為基礎(chǔ)通信組件,用于實(shí)現(xiàn)各進(jìn)程節(jié)點(diǎn)之間的內(nèi)部通信。

2)游戲行業(yè):
無論是手游服務(wù)端還是大型的網(wǎng)絡(luò)游戲,Java 語(yǔ)言得到了越來越廣泛的應(yīng)用。
Netty 作為高性能的基礎(chǔ)通信組件,它本身提供了 TCP/UDP 和 HTTP 協(xié)議棧。
非常方便定制和開發(fā)私有協(xié)議棧,賬號(hào)登錄服務(wù)器,地圖服務(wù)器之間可以方便的通過 Netty 進(jìn)行高性能的通信。

3)大數(shù)據(jù)領(lǐng)域:
經(jīng)典的 Hadoop 的高性能通信和序列化組件 Avro 的 RPC 框架,
默認(rèn)采用 Netty 進(jìn)行跨界點(diǎn)通信,它的 Netty Service 基于 Netty 框架二次封裝實(shí)現(xiàn)。

https://netty.io/wiki/related-projects.html (Netty相關(guān)工程)

4.2 實(shí)用功能

開發(fā)異步、非阻塞的 TCP 網(wǎng)絡(luò)應(yīng)用程序;
開發(fā)異步、非阻塞的 UDP 網(wǎng)絡(luò)應(yīng)用程序;
開發(fā)異步文件傳輸應(yīng)用程序;
開發(fā)異步 HTTP 服務(wù)端和客戶端應(yīng)用程序;
提供對(duì)多種編解碼框架的集成,包括谷歌的 Protobuf、Jbossmarshalling、Java 序列化、壓縮編解碼、XML 解碼、字符串編解碼等,這些編解碼框架可以被用戶直接使用;
提供形式多樣的編解碼基礎(chǔ)類庫(kù),可以非常方便的實(shí)現(xiàn)私有協(xié)議棧編解碼框架的二次定制和開發(fā);
基于職責(zé)鏈模式的 Pipeline-Handler 機(jī)制,用戶可以非常方便的對(duì)網(wǎng)絡(luò)事件進(jìn)行攔截和定制;
所有的 IO 操作都是異步的,用戶可以通過 Future-Listener 機(jī)制主動(dòng) Get 結(jié)果或者由 IO 線程操作完成之后主動(dòng) Notify 結(jié)果,用戶的業(yè)務(wù)線程不需要同步等待;
IP 黑白名單控制;
打印消息碼流;
流量控制和整形;
性能統(tǒng)計(jì);
基于鏈路空閑事件檢測(cè)的心跳檢測(cè)
……

參考資源
https://netty.io/
https://developers.google.cn/protocol-buffers/
https://www.grpc.io/
李林鋒. (2015). Netty權(quán)威指南(第2版).
http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf (Scalable IO in Java)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 包含的重點(diǎn)內(nèi)容:JAVA基礎(chǔ)JVM 知識(shí)開源框架知識(shí)操作系統(tǒng)多線程TCP 與 HTTP架構(gòu)設(shè)計(jì)與分布式算法數(shù)據(jù)庫(kù)知...
    消失er閱讀 4,552評(píng)論 1 10
  • 本系列出于AWeiLoveAndroid的分享,在此感謝,再結(jié)合自身經(jīng)驗(yàn)查漏補(bǔ)缺,完善答案。以成系統(tǒng)。 Java基...
    濟(jì)公大將閱讀 1,618評(píng)論 1 6
  • 原創(chuàng)鏈接 一、Java面試題java有多重要,對(duì)于做android的我們,不需要多說了,let’s go (1)J...
    李福來閱讀 2,443評(píng)論 0 5
  • 我麻衣朝天子的王 望著你 我喜極而泣 找遍了地圖 安放不下你的一場(chǎng)痛哭 你在錦城春夜喜雨嗎 黃四娘家花滿蹊未 你在...
    火流蘇_閱讀 317評(píng)論 0 3
  • 12月15日,馮小剛的《芳華》強(qiáng)勢(shì)回歸,打響了賀歲檔突圍戰(zhàn)的第一槍,拉開了賀歲檔的帷幕。隨后各方積極響應(yīng),紛紛加入...
    古院雜談閱讀 372評(píng)論 0 1

友情鏈接更多精彩內(nèi)容