1.源碼準備
試用版本為
引用包版本
Netty 服務(wù)端創(chuàng)建的時序圖,如下:
服務(wù)端創(chuàng)建的時序圖
2.服務(wù)端啟動代碼說明:
舉例:服務(wù)端啟動代碼:
服務(wù)端啟動代碼
開啟一個服務(wù)端颜说,端口綁定在8888,使用nio模式,下面講下每一個步驟的處理細節(jié)
1)EventLoopGroup
是一個死循環(huán)躲撰,不停地檢測IO事件,處理IO事件钓觉,執(zhí)行任務(wù)茴肥,后面詳細描述;
初始化用于Acceptor的主"線程池"以及用于I/O工作的從"線程池"荡灾;
2)ServerBootstrap
初始化ServerBootstrap實例瓤狐, 此實例是netty服務(wù)端應(yīng)用開發(fā)的入口是服務(wù)端的一個啟動輔助類;
通過給它設(shè)置一系列參數(shù)來綁定端口啟動服務(wù);
3).channel(NioServerSocketChannel.class)
指定通道channel的類型批幌,由于是服務(wù)端础锐,故而是NioServerSocketChannel;
表示服務(wù)端啟動的是nio相關(guān)的channel荧缘;
channel在netty里面是一大核心概念皆警,可以理解為一條channel就是一個連接或者一個服務(wù)端bind動作;
4)b.childHandler(new NettyServerFilter())
表示一條新的連接進來之后,該怎么處理截粗,
NettyServerFilter代碼如圖中所示:
NettyServerFilter代碼
5)ChannelFuturef =b.bind(port).sync()
這里就是真正的啟動過程了信姓,綁定6789端口,等待服務(wù)器啟動完畢绸罗,才會進入下行代碼意推。
3. 詳細描述
跳入bind()方法:
bind()方法
通過端口號創(chuàng)建一個 InetSocketAddress,然后繼續(xù)step珊蟀,進入下一步:
image.png
其中validate()方法用于驗證服務(wù)啟動需要的必要參數(shù)是否合格
ivalidate()
跳入上一層校驗菊值,校驗內(nèi)容如下,group及channelFactory是否為空育灸;
image.png
完成校驗以后進行doBind()方法:
doBind()
dobind()內(nèi)部實現(xiàn)腻窒,主要有兩個核心內(nèi)容。兩大核心一個是 initAndRegister()磅崭,以及doBind0();
a)首先看initAndRegister() 方法:
initAndRegister() 方法
核心代碼如圖中箭頭部分儿子;
1. new 一個channel,
2. init這個channel砸喻,即調(diào)用init(channel)初始化通道信息
3. 將這個channel register到某個對象柔逼。
<1> new 一個channel,step進入
channel = channelFactory().newChannel();此處相比以前版本去掉final并放入try-catch中
調(diào)用channelFactory生成通道channel實例恩够,
NioServerSocketChannel 作為clazz卒落,是通過serverbootstrap的channel方法來指定通道類型羡铲。
進一步查看channleFactory的初始化蜂桶,
此時回到NettyServer, Netty實現(xiàn)初始化AbstractBootstrap的位置也切,此時扑媚,在代碼中存在
b.channel(NioServerSocketChannel.class); // 設(shè)置nio類型的channel
查找此方法腰湾,發(fā)現(xiàn)在AbstractBootstrap
類中存在channel方法,在方法channel中初始化new一個factory:
channelFactory初始化
ReflectiveChannelFactory繼承了ChannelFactory工廠方法newChannel
所以疆股,initAndRegister中的channelFactory.newChannel()方法就是生成了一個NioServerSocketChannel的實例费坊。
image.png
clazz.newInstance() 是通過反射的方式來創(chuàng)建一個對象,
而這個clazz就是我們在ServerBootstrap中傳入的NioServerSocketChannel.class
進一步step旬痹,會初始化一系列變量附井,最終調(diào)用反射的clazz類,即NioServerSocketChannel两残,進行初始化永毅,
NioServerSocketChannel初始化
private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
//DEFAULT_SELECTOR_PROVIDER 為一個selector,
逐漸step人弓,會跳入到NioServerSocketChannel構(gòu)造方法中沼死,
NioServerSocketChannel構(gòu)造方法
[NIO SelectionKey中定義的4種事件]
* SelectionKey.OP_ACCEPT —— 接收連接繼續(xù)事件,**表示服務(wù)器監(jiān)聽到了客戶連接崔赌,服務(wù)器可以接收這個連接了**
* SelectionKey.OP_CONNECT —— 連接就緒事件意蛀,**表示客戶與服務(wù)器的連接已經(jīng)建立成功**
* SelectionKey.OP_READ —— 讀**就緒**事件,**表示通道中已經(jīng)有了可讀的數(shù)據(jù)健芭,可以執(zhí)行讀操作了(通道目前有數(shù)據(jù)县钥,可以進行讀操作了)**
* SelectionKey.OP_WRITE —— 寫**就緒**事件,**表示已經(jīng)可以向通道寫數(shù)據(jù)了(通道目前可以用于寫操作)**
這里 注意吟榴,下面兩種魁蒜,SelectionKey.OP_READ ,SelectionKey.OP_WRITE 吩翻,
1.當向通道中注冊SelectionKey.OP_READ事件后兜看,如果客戶端有向緩存中write數(shù)據(jù),下次輪詢時狭瞎,則會 isReadable()=true细移;
2.當向通道中注冊SelectionKey.OP_WRITE事件后,這時你會發(fā)現(xiàn)當前輪詢線程中isWritable()一直為ture熊锭,如果不設(shè)置為其他事件
進一步往構(gòu)造方法super上層查看弧轧,最后跳入:
image.png
將前面 provider.openServerSocketChannel(); 創(chuàng)建出來的 ServerSocketChannel保存到成員變量ch
然后調(diào)用ch.configureBlocking(false);設(shè)置該channel為非阻塞模式
這里的 readInterestOp 即前面層層傳入的 SelectionKey.OP_ACCEPT,
接下來重點分析 super(parent);(這里的parent其實是null碗殷,由前面寫死傳入)精绎;
在AbstractNioChannel中做了下面幾件事:
1、繼續(xù)調(diào)用父類AbstractChannel(Channel parent)構(gòu)造方法锌妻;
此構(gòu)造方法中代乃,主要做了三件事:
1)、給channel生成一個新的id
2)、通過newUnsafe初始化channel的unsafe屬性
3)搁吓、pipeline =new DefaultChannelPipeline(this) 初始化channel的pipeline屬性
image.png
2)在AbstractChannel類中原茅,newUnsafe()是一個抽象方法
image.png
最終實現(xiàn)來自于AbstractNioMessageChannel類中有newUnsafe()的實現(xiàn)
image.png
此方法返回一個NioMessageUnsafe實例對象,
而NioMessageUnsafe是AbstractNioMessageChannel的內(nèi)部類
NioMessageUnsafe 只覆蓋了 父類AbstractNioUnsafe中的read方法堕仔,如下圖
通過NioMessageUnsafe 及其父類的代碼便可以知道擂橘,
其實unsafe對象是真正的負責底層channel的連接/讀/寫等操作的,
unsafe就好比一個底層channel操作的代理對象
NioMessageUnsafe
OP_ACCEPT都已經(jīng)注冊上了摩骨,當接收到新用戶連接時就會觸發(fā)unsafe.read()方法通贞。
read()會不斷調(diào)用doReadMessages(),將產(chǎn)生的readBuf逐一發(fā)送給Pipeline.fireChannelRead()去處理恼五。
private final class NioMessageUnsafe extends AbstractNioUnsafe {
private final List<Object> readBuf = new ArrayList<Object>();
@Override
public void read() {
assert eventLoop().inEventLoop();
final ChannelConfig config = config();
final ChannelPipeline pipeline = pipeline();
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
try {
do {
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
}
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
} catch (Throwable t) {
exception = t;
}
int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
if (exception != null) {
closed = closeOnReadError(exception);
pipeline.fireExceptionCaught(exception);
}
if (closed) {
inputShutdown = true;
if (isOpen()) {
close(voidPromise());
}
}
} finally {
// Check if there is a readPending which was not processed yet.
// This could be for two reasons:
// * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
// * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
//
// See https://github.com/netty/netty/issues/2254
if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
}
unsafe內(nèi)容后續(xù)學習滑频;
3) pipeline =new DefaultChannelPipeline(this);
# step之后發(fā)現(xiàn),最后實現(xiàn)如下圖代碼所示:
# 初始化了HeadContext及TailContext對象唤冈。
# head及tail初始化完成后峡迷,它們會相互連接。
# 通過上面的代碼可以得出你虹,pipeline就是一個雙向鏈表绘搞。
DefaultChannelPipeline構(gòu)造函數(shù)
<1>部分總結(jié):
用戶調(diào)用方法 Bootstrap.bind(port) 第一步就是通過反射的方式new一個NioServerSocketChannel對象,
并且在new的過程中創(chuàng)建了一系列的核心組件傅物,進一步研究:
1夯辖、NioServerSocketChannel對象內(nèi)部綁定了Java NIO創(chuàng)建的ServerSocketChannel對象;
2董饰、Netty中蒿褂,每個channel都有一個unsafe對象,此對象封裝了Java NIO底層channel的操作細節(jié)卒暂;
3啄栓、Netty中,每個channel都有一個pipeline對象也祠,此對象就是一個雙向鏈表昙楚;
NioServerSocketChannel的類繼承結(jié)構(gòu)圖:
NioServerSocketChannel的類繼承結(jié)構(gòu)圖
<2> init這個channel
image.png
上面代碼前幾行主要進行配置,
重要內(nèi)容為最后一行
獲取當前通道的pipeline诈嘿,然后為 NioServerSocketChanne l綁定的 pipeline 添加 Handler堪旧;
此pipeline即為上面<2>中生成的pipeline