一、什麼是@Sharable
首先我們看下如下的代碼:
public class Server {
public static void main(String[] args) {
NioEventLoopGroup boss = new NioEventLoopGroup(1);
NioEventLoopGroup worker = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.group(boss, worker);
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024, 12, 4, 0, 0));
//引入編解碼器
ch.pipeline().addLast(new MessageCodec());
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//打印消息內容
System.out.println(msg);
super.channelRead(ctx, msg);
}
});
}
});
ChannelFuture channelFuture = serverBootstrap.bind(8080);
//阻塞等待連接
channelFuture.sync();
//阻塞等待釋放連接
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
System.out.println("server error:" + e);
} finally {
// 釋放EventLoopGroup
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
在上述的代碼當中,有三個handler,而這三個handler是針對每一個SocketChannel 的,當連接數很多的時候,可能需要創建大量的handler實例,佔據大量的內存空間。
那麼是否能夠將handler提出來作爲公共使用呢?
當然是可以的,但是要考慮兩種handler是否存在線程安全問題。
這裏就可以使用這個註解@Sharable來表示。
其註釋的含義如下所示:
表示可以將帶註釋的ChannelHandler的同一實例多次添加到一個或多個ChannelPipeline ,而不會出現競爭條件。
如果未指定此註解,則每次將其添加到管道時都必須創建一個新的處理程序實例,因爲它具有成員變量等非共享狀態。
二、使用@Sharable
首先我們看下前一章節當中自己創建的編解碼器,添加上該註解後,是否能夠正常使用。
/**
* @description: 消息編解碼器
* @author:weirx
* @date:2021/11/16 14:49
* @version:3.0
*/
@ChannelHandler.Sharable
public class MessageCodec extends ByteToMessageCodec<Message> {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, Message msg, ByteBuf out) throws Exception {
// 4 字節的魔數
out.writeBytes(new byte[]{1, 2, 3, 4});
// 1 字節的版本,
out.writeByte(1);
// 1 字節的序列化方式 0:json
out.writeByte(0);
// 1 字節的指令類型
out.writeByte(msg.getMessageType());
// 4 個字節的請求序號
out.writeInt(msg.getSequenceId());
// 無意義,對齊填充,使其滿足2的n次方
out.writeByte(0xff);
// 獲取內容的字節數組
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(msg);
byte[] bytes = bos.toByteArray();
// 長度
out.writeInt(bytes.length);
// 寫入內容
out.writeBytes(bytes);
}
@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> out) throws Exception {
int magicNum = in.readInt();
byte version = in.readByte();
byte serializerType = in.readByte();
byte messageType = in.readByte();
int sequenceId = in.readInt();
in.readByte();
int length = in.readInt();
byte[] bytes = new byte[length];
in.readBytes(bytes, 0, length);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
Message message = (Message) ois.readObject();
System.out.println("decode:" + magicNum + "," + version + "," + serializerType + "," + messageType + "," + sequenceId + "," + length);
System.out.println("decode:" + message);
out.add(message);
}
}
添加上之後就㞏正常啓動客戶端了。分析一下問題,看MessageCodec 繼承了ByteToMessageCodec,我們跟蹤源碼進去看看。因爲我們的這個類沒有構造方法,所以直接去看父類ByteToMessageCodec的構造方法:
protected ByteToMessageCodec(boolean preferDirect) {
ensureNotSharable();
outboundMsgMatcher = TypeParameterMatcher.find(this, ByteToMessageCodec.class, "I");
encoder = new Encoder(preferDirect);
}
有一個 ensureNotSharable()的方法,直譯過來就是確保不是可共享的。
protected void ensureNotSharable() {
if (isSharable()) {
throw new IllegalStateException("ChannelHandler " + getClass().getName() + " is not allowed to be shared");
}
}
如果是,就拋出異常。
其實在ByteToMessageCodec這個類的註釋上就表明了這個類的子類必須不是可共享的。如下所示。
/**
* A Codec for on-the-fly encoding/decoding of bytes to messages and vise-versa.
*
* This can be thought of as a combination of {@link ByteToMessageDecoder} and {@link MessageToByteEncoder}.
*
* Be aware that sub-classes of {@link ByteToMessageCodec} <strong>MUST NOT</strong>
* annotated with {@link @Sharable}.
* 子類ByteToMessageCodec必須不@Sharable註解。
*/
public abstract class ByteToMessageCodec<I> extends ChannelDuplexHandler
那麼想讓我們的handler變成可共享的要怎麼辦 ?
第一、只要保證我們的handler沒有成員變量等非共享狀態。
第二、修改繼承的父類,對於編解碼器類,不能繼承 ByteToMessageCodec 或 CombinedChannelDuplexHandler 父類,他們的構造方法對 @Sharable 有限制。
第三、可以使用MessageToMessageCodec 父類作爲父類。
滿足以上就可以讓我們自己的handler使用@Sharable註解了。
本章就到此爲止,有用的話,點個贊再走吧~~