什么是@Shareable?
@Inherited
@Documented
@Target(value=TYPE)
@Retention(value=RUNTIME)
public static @interface ChannelHandler.Sharable
Indicates that the same instance of the annotated ChannelHandler can be added to one or more ChannelPipelines multiple times without a race condition.
If this annotation is not specified, you have to create a new handler instance every time you add it to a pipeline because it has unshared state such as member variables.
This annotation is provided for documentation purpose, just like the JCIP annotations.
标识同一个ChannelHandler的实例可以被多次添加到多个ChannelPipelines中,而且不会出现竞争条件。
如果一个ChannelHandler没有标志@Shareable,在添加到到一个pipeline中时,你需要每次都创建一个新的handler实例,因为它的成员变量是不可分享的。
这个注解仅作为文档参考使用,比如说JCIP注解。
解释
实际上意思就是说,这个注解在实际运行时,是起不到什么作用的。它只是用来告诉你,或者你用它来标识一个handler,这个handler能不能被多个pipeline安全地共享。
代码
- 一个可以被共享的echo handler。
/**
* Created by Anur IjuoKaruKas on 2018/6/1
*/
@Sharable// 表示它可以被多个channel安全地共享
public class ShareableEchoServerHandler extends ChannelInboundHandlerAdapter {
private AtomicInteger integer = new AtomicInteger(0);
public ShareableEchoServerHandler() {
System.out.println(this.getClass()
.getSimpleName() + " init....");
}
// 从channel中读取消息时
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println(integer.incrementAndGet());
ByteBuf in = (ByteBuf) msg;
System.out.println(
"Server received: " + in.toString(CharsetUtil.UTF_8));
ctx.write(in);
}
// 读取完毕后的处理
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);// 关闭该Channel
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx,
Throwable cause) {
cause.printStackTrace();
ctx.close(); // 关闭该channel
}
}
- EchoServer
/**
* Created by Anur IjuoKaruKas on 2018/6/1
*/
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public static void main(String[] args) throws Exception {
new EchoServer(20000).start();
}
public void start() throws Exception {
final ShareableEchoServerHandler serverHandler
= new ShareableEchoServerHandler();// 可以被共享的handler
EventLoopGroup group = new NioEventLoopGroup();// 创建 EventLoopGroup
try {
ServerBootstrap b = new ServerBootstrap();// 创建ServerBootstrap
b.group(group)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline()
.addLast(serverHandler);// ShareableEchoServerHandler 被标注为@Shareable,所以我们可以总是使用同样的实例
// .addLast(new NoShareableEchoServerHandler());
}
});
ChannelFuture f = b.bind()
.sync();
f.channel()
.closeFuture()
.sync();
} finally {
group.shutdownGracefully()
.sync();
}
}
}
代码中,我们往尾部添加了这个serverHandler handler,它只需要被初始化一次就可以了。通常来说,统计流量之类的handler可以采用这种方法,让全局共用同一个handler。
- 代码输出:
1
Server received: A msg from sokit
2
Server received: A msg from sokit
3
Server received: A msg from sokit
可以看到,发送了三次消息,使用的都是同一个handler。handler中的计数服务一直在递增。
- 使用这种线程共享的handler可以避免频繁创建handler带来的系统开销
- 适用于某些支持线程共享的handler,比如日志服务,计数服务等。
- 适用于没有成员变量的encoder、decoder
不使用Shareable
改造EchoServer代码
b.group(group) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress(port)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) { ch.pipeline() .addLast(new ShareableEchoServerHandler());// ShareableEchoServerHandler 被标注为@Shareable,所以我们可以总是使用同样的实例 } });
输出
1
Server received: A msg from sokit
ShareableEchoServerHandler init....
1
Server received: A msg from sokit
ShareableEchoServerHandler init....
1
Server received: A msg from sokit
我们可以看到,handler会在pipeline中被重复创建。