談?wù)勀銓?duì)Netty中,Pipeline工作原理的理解?
1位工作8年的小伙伴,去某東面試IM部門,被問到這樣一道面試題。說,請(qǐng)你談一談你對(duì)Netty Pipeline設(shè)計(jì)原理的理解。當(dāng)時(shí),他說只是用過Netty的Pipline,原理沒有深入了解過,然后就沒有然后了。
今天,我給大家講一講,我對(duì)Netty Pipeline的理解。
1、結(jié)構(gòu)設(shè)計(jì)
Netty中的Pipeline本質(zhì)上是一個(gè)雙向鏈表,它采用了責(zé)任鏈模式。在Netty中每個(gè)Channel都有且僅有一個(gè)ChannelPipeline與之對(duì)應(yīng),它們的組成關(guān)系如下圖所示。
NEW
通過上圖可以看到,一個(gè)Channel包含了一個(gè)ChannelPipeline,而ChannelPipeline中又維護(hù)了一個(gè)由ChannelHandlerContext組成的雙向鏈表。這個(gè)鏈表的頭叫HeadContext,鏈表的尾叫TailContext,并且每個(gè)ChannelHandlerContext中又關(guān)聯(lián)著一個(gè)ChannelHandler。
2、工作原理
首先來看這樣一段代碼:
NEW
Bootstrap client = new Bootstrap(); client.channel(NioSocketChannel.class) .handler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { //接收課客戶端請(qǐng)求的處理流程 ChannelPipeline pipeline = ch.pipeline(); //對(duì)象編碼器 pipeline.addLast("encoder",new ObjectEncoder()); //對(duì)象解碼器 pipeline.addLast("encoder",new ObjectDecoder()); } });
對(duì)于用過Netty的小伙伴來說,應(yīng)該非常熟悉。在Netty中,Pipeline的初始化,是通過調(diào)用Channel的handler()方法,然后在handler()方法中傳入一個(gè)叫做ChannelInitializer的對(duì)象,通過SocketChannel構(gòu)建出一個(gè)新的Pipeline對(duì)象。每次調(diào)用addLast()方法,都會(huì)在Pipelie的末端插入一個(gè)ChannelHandlerContext。如圖所示:
NEW
每個(gè)Context中又會(huì)包含一個(gè)ChannelHandler,我們通過addLast()方法往Pipeline中添加的對(duì)象,并且Handler的添加順序會(huì)影響代碼的執(zhí)行順序。而這些Handler本質(zhì)上都是實(shí)現(xiàn)編碼和解碼的功能,不管是編碼器還是解碼器都必須實(shí)現(xiàn)ChannelHandler接口。
圖中的Handler就是我們代碼程序要執(zhí)行的邏輯。而Netty默認(rèn)幫我們實(shí)現(xiàn)了非常多內(nèi)置Handler,我們只需要直接拿過來用就可以了。當(dāng)然,我們也可以自己實(shí)現(xiàn)ChannelHandler接口,來實(shí)現(xiàn)自定義的編、碼器。比如自定義通信協(xié)議等等。
當(dāng)所有的Handler全部添加到Pipeline中以后,Netty就會(huì)將這些Handler組裝成一個(gè)雙向鏈表,從而實(shí)現(xiàn)串行化調(diào)用。從頭部往尾部執(zhí)行的Handler被稱為Inbound,用來接收用戶請(qǐng)求,從尾部往頭部執(zhí)行的Handler被稱為Outbound,用來給用戶響應(yīng)。所以,Inbound可以用來實(shí)現(xiàn)解碼的功能、而Outbound可以用來實(shí)現(xiàn)編碼的功能。
好了,以上就是我對(duì)Pipeline設(shè)計(jì)原理的理解。