netty(十九)ChannelInitializer 使用公共handler(@Shareable)實踐及邏輯解答【重點】


在 netty channel的線程安全性與@Sharable  中討論了ChannelInitializer,Pipeline,@Shareable,本質就2點:

1)ChannelInitializer可以實現每個連接創建一個pipeline,而且pipeline內的handler,每個連接都能有個新的sethandler

這個地方會有過誤解:其實不是你用了ChannelInitializer,就是每個連接都有一個新的handler了,你也可以在ChannelInitializer中設置一個公共對象(@Shareable修飾,意為:可共享的)

當然對於這樣一個對象,書上說的不全准確,比起對象的線程安全性,更重要的是你得確定它是關於連接無狀態的,比如StringDecoder,不能是LengthDecoder這樣的

2)channel屬於一個線程,ChannelPipeline屬於一個channel,所以對ChannelPipeline的操作始終在一個線程內,可以隨意remove add而不用考慮同一時刻,有另外一個線程在操作pipeline,因為對一個channel的操作,netty承諾始終在一個線程中

 

對第2)點,有一個案例 netty tcp(ws)鑒權2個方案

本文對第1)點,嘗試一個案例,在netty粘包(一)消息定長 實踐的代碼基礎上,使ChannelInitializer中的handler為同一對象

一 多個handler

            ChannelHandler [] channelHandlers = {
                    new FixedLengthFrameDecoder(14),
                    new StringDecoder(),
                    new StringEncoder(),
                    new ServerHandler4(),
                    new ServerHandler5()
            };

            //設置管道工廠
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {

                // 多個handler
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                    //獲取管道
                    ChannelPipeline pipeline = socketChannel.pipeline();
                    //定長解碼類
                    pipeline.addLast(channelHandlers[0]);
                    //字符串解碼類
                    pipeline.addLast(channelHandlers[1]);

                    pipeline.addLast(channelHandlers[2]);
                    //處理類
                    pipeline.addLast(channelHandlers[3]);
                    pipeline.addLast(channelHandlers[4]);

                }
            });

 

二 單個handler

            // 單個handler
            bootstrap.childHandler(new SimpleChannelInboundHandler<ByteBuf>() {
                @Override
                protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
                    System.out.println("Received data");
                }
            });

 

第一個client連接

a 單個handler輸出

server start ......
Received data

與預期一致,單handler服務端沾包了,只打印了一條

b 多個handler按5條正常輸出

 

當第二個client連接時

十二月 17, 2019 9:34:19 下午 io.netty.channel.ChannelInitializer channelRegistered
警告: Failed to initialize a channel. Closing: [id: 0x08956454, /127.0.0.1:58422 => /127.0.0.1:8866]
io.netty.channel.ChannelPipelineException: io.netty.handler.codec.FixedLengthFrameDecoder is not a @Sharable handler, so can't be added or removed multiple times.
at io.netty.channel.DefaultChannelPipeline.checkMultiplicity(DefaultChannelPipeline.java:461)
at io.netty.channel.DefaultChannelPipeline.addLast0(DefaultChannelPipeline.java:138)
at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:131)
at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:258)
at io.netty.channel.DefaultChannelPipeline.addLast(DefaultChannelPipeline.java:245)
at com.jds.test.stringlength.Server5$1.initChannel(Server5.java:47)

 -=======-

十二月 17, 2019 10:10:26 下午 io.netty.channel.DefaultChannelPipeline$TailHandler exceptionCaught
警告: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.netty.channel.ChannelPipelineException: com.jds.test.stringlength.Server6$1 is not a @Sharable handler, so can't be added or removed multiple times.

 

io.netty.handler.codec.FixedLengthFrameDecoder這是一個連接相關的handler,是不能共用的,每個連接應保持內部讀取的字節狀態以處理沾包,它不像StringDecoder和StringEncoder,連接無關,輸入參byte數組,輸出字符串,可以共用,事實上netty也將這兩個ChannelHandlerAdapter聲明為@Shareable


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM