Netty , Sharable

Netty , Sharable

Sharable的源码

Sharable是ChannelHandler接口内部的注解。ChannelHandler的实现类加了这个注解,这个实现类的实例可以被添加到多个ChannelPipeline中,前提是这个实现类内部是没有状态的,否则,会有线程安全问题。下面是源码:

Sharable注解存在的意义

一个ChannelHandler实例可以被添加到多个ChannelPipeline中

在重连服务器时,客户端再一次成功重连到服务器时,会调用ChannelInitializer#initChannel方法,在这个方法中会再一次往pipe里加入一些ChannelHandler,如解码器、编码器、消息处理器等。其中有些ChannelHandler是没有状态的,那么在重连成功后,可以往pipe里加入上次的ChannelHandler实例,这样就不需要再重新创建一个对象了。

提醒使用者,一个ChannelHandler是被多个ChannelPipeline共享,如果ChannelHandler里有状态,需要自己维护线程安全问题

这个提醒在netty中ByteToMessageDecoder就有体现,下面是截取了部分代码。

从上面的代码可以看出ByteToMessageDecoder类中有成员变量 ByteBuf cumulation。设想一个场景,ByteToMessageDecoder构造方法里没有调用ensureNotSharable这个方法,并且ByteToMessageDecoder的实现类加了@Sharable注解,同时被加入到多个ChannelPipeline(可以理解为多个连接),在多个ChannelPipeline(多个线程)中使用这个实例,可能一个ChannelPipeline(线程)的ByteBuf cumulation读的是一个位置,另一个ChannelPipeline(线程)的ByteBuf cumulation读的是另一个位置,这样会产生竞态条件。由于会存在线程安全问题,所以ByteToMessageDecoder在构造方法里使用ensureNotSharable验证是否加了Sharable注解。每个实现了ChannelHandler的实例,是否能被加入多个ChannelPipeline,是由实现者自己去维护,父类ChannelHandlerAdapter只是提供了ensureNotSharable这个方法。ChannelHandlerAdapter部分代码如下:

举一个没有验证Sharable注解的实现类 MessageToMessageDecoder,构造函数里没有验证,部分代码如下:

ChannelPipeline 添加 ChannelHandler的代码分析

ChannelPipeline里抽象了如何添加一个ChannelHandler,如addFirst,addLast,addAfter。这些add方法中,都会调用checkMultiplicity方法,检测一个ChannelHandler实例是否被多个ChannelPipeline添加,DefaultChannelPipeline#addFirst部分代码如下:

从checkMultiplicity方法中,可以看出一个ChannelHandler只要被加入到ChannelPipeline,h.added就会true,标记为已被添加过。同时,如果这个实例被添加过了,是ChannelHandlerAdapter的子类,也加了Sharable注解,那么是可以被多个ChannelPipeline成功添加的。

结语

Netty的@Sharable解决了使用者想把一个实例添加到多个ChannelPipeline的需求,也提醒使用者,加了@Sharable,线程安全问题需要自己维护。

JackLei
JackLei

我是真的不会修电脑