在 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