netty之——@Shareable注解

什么是@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中被重复创建。

猜你喜欢

转载自blog.csdn.net/anurnomeru/article/details/80537128