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

posted on 2019-12-17 19:41  silyvin  阅读(2024)  评论(0编辑  收藏  举报