1. 程式人生 > >netty 4&5之學習之路_1

netty 4&5之學習之路_1

                                                                              **netty的執行緒模型**。 

                        

                        netty繫結埠後,開始監聽連線。

             當一個連線進來後,先是由boss執行緒來處理accept的操作(也就是nioeventloop排程boss

執行緒處理,後面會細講),當accept成功後則可以互相通訊了。如果有資料進來則將連線交給work

執行緒來執行讀的操作,netty將資料處理為Bytebuf形式交給(其實是交給channelpipeline並且由它

來呼叫channelhandler)socketchannnel中的channelhandler(在連線成功後會觸發channelhandler

的channelActive方法,在這裡可以做一些初始的工作),這就是為什麼說boss執行緒池處理連線,

work執行緒池處理io讀寫,也被說為io讀寫執行緒池。

             當把資料交給 work執行緒,通常會對進來的資料解碼,這就會用到解碼器,在筆者的一篇

文章中詳細講解了解碼器的基礎,有讀者不怎麼了解的可以去看看。資料編碼後,netty會將資料傳

給下一個handler.一般我們自己寫的handler會放在最後一個,裡面有我們業務操作的程式碼,一般項

目中在這裡也是將資料投入業務處理池的位置。最後業務處理好後會想要把資料傳送給客戶端,所

以又會經過outhandler對資料進行解碼給底層socket傳送,這個操作又會交給work執行緒完成,work

執行緒會呼叫channel進行寫操作即傳送資料給

客戶端。(也就是channel底層socket傳送資料)

            當客戶端斷開連線後又會觸發channelhandler的channelInActive方法。學習過Java nio的讀

者應該知道reactor模式,netty也是這個模式。Java nio 中有一個selector,即多路複用器。在netty

中nioeventloopGroup充當selector的角色,nioeventloop是排程類,他負責著netty大大小小事情,

netty是事件驅動的非同步框架,所以在事件驅動中也有它的影子。nioeventloopGroup包含一個或多

個nioeventloop。(本篇部落格是對netty大體框架進行一個分析,細節之後部落格會有詳解,比如這個

排程的細節)。

一個連線對應這一個channel,一個channel對應一個nioeventloop,但是一個nioeventloop對應一

個或者多個channel。Java nio是單執行緒排程,而netty是多執行緒維護有著更好的併發處理效能。

public class Server {

    public void stratServer(int port) throws InterruptedException {

        EventLoopGroup boss = new NioEventLoopGroup();//裡面維護這boss執行緒池;
        EventLoopGroup work = new NioEventLoopGroup();//裡面維護這work執行緒池


        try {
            ServerBootstrap sp = new ServerBootstrap();//netty啟動輔助類

            sp.group(boss, work);
sp.channel(NioServerSocketChannel.class);//涉及到netty啟動時用那個來初始化channel,在不同模式下用的類不同,一般都是NioServerSocketChannel
            sp.option(ChannelOption.SO_BACKLOG, 1024); // 連線數
            sp.option(ChannelOption.TCP_NODELAY, true); // 不延遲,訊息立即傳送//
            sp.childOption(ChannelOption.SO_KEEPALIVE, true); // 長連線
            sp.childHandler(new ChannelInitializer<SocketChannel>() {// 在初始化channel的時候會用到這個,呼叫initchannel方法裝載handler,並且剔除自身即ChannelInitializer(本身也是個handler)

                        @Override
                        protected void initChannel(SocketChannel ch)
                                throws Exception {
                            ByteBuf tr=Unpooled.copiedBuffer("@".getBytes());
                            ch.pipeline().addLast(new DelimiterBasedFrameDecoder(2048,tr));//netty自帶的解碼器
                            ch.pipeline().addLast(new Handler());//使用者自定義的解碼器
                        }
                    });
            sp.bind(port).sync().channel().closeFuture().sync();//繫結埠,這個看起來嚇人其實就是繫結埠的操作sync就是堵塞的意思,當操作完成後才不堵塞。,比如第一個sync方法指一直阻塞到bind操
完成。第二個指一直阻塞到channel關閉後才不阻塞。
        } catch (Exception e) {

        } finally {
            //完美的釋放記憶體
            boss.shutdownGracefully();
            work.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new Server().stratServer(11232);
    }
}


下面這個就是筆者自定義的handler,業務入口就在channelRead0方法中。


public class Handler  extends SimpleChannelInboundHandler<Object>{


    /**
     * JFree_Wolf
     */
        @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {//接受資料,處理資料
        ByteBuf by=(ByteBuf)msg;
        System.out.println(ctx.channel().remoteAddress().toString());
        System.out.println("by大小為 "+by.readableBytes());
        //ReferenceCountUtil.release("12");
        //ctx.executor();
    //  ctx.channel().config().setWriteBufferLowWaterMark(12);
        ctx.channel().writeAndFlush("123\n").addListener(new ChannelFutureListener(){
            public void operationComplete(ChannelFuture future) throws Exception {
                System.out.println("IO操作完成");
            }
        });
    //  ctx.fireChannelRead(msg);
    }
     public void channelActive(ChannelHandlerContext ctx) throws Exception {
         System.out.println("================success connect");
         super.channelActive(ctx);
     }
       @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            System.out.println("server niosokcetchannel close ");
            System.out.println(t);
            super.channelInactive(ctx);
        }
       @Override
       public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause){
           cause.printStackTrace();
           //handler發生錯誤則會呼叫次方法。
           ctx.close();
       }
}
           這是筆者學習netty時寫的demo,用來研究 DelimiterBasedFrameDecoder解碼器的

用法的。 在這裡推薦新手不要直接複製,這樣對你的理解沒有幫助,哪怕你照著程式碼看註釋敲

一遍都對你理解比較好。學習Java,敲程式碼是必須的,哪怕照著敲一遍!另外這篇部落格是筆者

對於netty大體框架的一個總結。但是很有很多沒有解釋,比如channelhadler,socketchannnel

的細節處理,他的父類,介面的用處,eventloop呼叫的細節,tcp/ip連線的細節 等等,這些在

之後有時間筆者會好好寫一下,和大家做個交流。