在 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承诺始终在一个线程中
本文对第1)点,尝试一个案例,在netty粘包(一)消息定长 实践的代码基础上,使ChannelInitializer中的handler为同一对象
一 多个handler
ChannelHandler [] channelHandlers = {
new FixedLengthFrameDecoder(14),
new StringDecoder(),
new StringEncoder(),
new ServerHandler4(),
new ServerHandler5()
};
//设置管道工厂
bootstrap.childHandler(new ChannelInitializer() {
// 多个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() {
@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