netty(十七)Netty提升 - 你不知道的@Sharabl
一、什么是@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注解了。
本章就到此为止,有用的话,点个赞再走吧~~