Netty源码-Sharable注解介绍及使用场景

[toc]

学习目标

  • 理解Sharable注解
  • 了解Sharable注解使用场景
  • 了解Sharable注解注意事项

Sharable注解介绍

个人臆想

只要在ChannelHandler上添加了@ChannelHandler.Sharable注解,所有的channel都共享这一个ChannelHandler实例。真是这样的吗?并且我还能猜到:“你认为所有的channel共享一个pipeline!”,如果你真是这么认为的,只能说明你对pipeline的初始化不理解。你臆想完全错误!!!

Sharable注解介绍

在我介绍Sharable注解之前,强烈推荐阅读下这篇文章《Netty源码-ChannelPipeline的剖析》,等你阅读完后,上面的问题迎刃而解。同时,你脑海中会出现另一个问题:Sharable注解存在的意义是什么?也就是它的使用场景。

言归正传,让我们来看看Sharable注解,还是从源码看起,看源码一定要看注释!一定要看注释!一定要看注释!,重要的事情说三遍,要不人家写注释干啥,有那时间干点啥不好!进入正题。

  /**
     * Indicates that the same instance of the annotated {@link ChannelHandler}
     * can be added to one or more {@link ChannelPipeline}s multiple times
     * without a race condition.
     * 在无线程竞争的条件下,带有Sharable注解channelHandler的同一个实例,
     * 可以被多次添加到一个或者多个ChannelPipeline中。
     * 
     * <p>
     * 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.
     * 如果没有指定这个注解的话,你把它添加到pipeline中时,每次都要创建一个handler实例,
     * 因为它存在不想共享状态,比如:成员变量。
     * <p>
     */
    @Inherited
    @Documented
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface Sharable {
        // no value
    }

这回应该知道为啥要看注释了吗?人家写很清楚。你ChannelHandler中的成员变量允许所有pipeline共享,你就用同一个实例,如果不想共享,那就每次都创建一个新实例。怎么写才是共享一个实例,怎么写才是每次创建一个新实例?

这样写是共享一个实例

StatusHandler statusHandler = new StatusHandler();
@Override
public void initChannelInitializer(SocketChannel ch) {
    ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
    ch.pipeline().addLast(new StringDecoder());
    ch.pipeline().addLast(statusHandler);
}

这样写是每次创建一个新实例

@Override
public void initChannelInitializer(SocketChannel ch) {
    ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
    ch.pipeline().addLast(new StringDecoder());
    ch.pipeline().addLast(new StatusHandler());
}

那我这样写呢?这样写也是每次创建一个新实例,知道为什么吗?不知道的话,再次推荐阅读下这篇文章《Netty源码-ChannelPipeline的剖析

@Override
public void initChannelInitializer(SocketChannel ch) {

    StatusHandler statusHandler = new StatusHandler();

    ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
    ch.pipeline().addLast(new StringDecoder());
    ch.pipeline().addLast(statusHandler);
}

Sharable注解使用场景

从命名上看,希望在pipeline之间能够共享一些信息。举几个例子,大家参考下,统计下当前的连接数,限流,白名单等等。

Sharable注解注意事项

在使用Sharable注解时,一定要考虑线程安全

实战-统计当前连接数

public class SharableServer extends AbstractNettyServer {

    StatusHandler statusHandler = new StatusHandler();
    @Override
    public void initChannelInitializer(SocketChannel ch) {
        ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
        ch.pipeline().addLast(new StringDecoder());
        ch.pipeline().addLast(statusHandler);
    }

    public static void main(String[] args) {
        Server server = new SharableServer();
        server.start();
    }
}

@ChannelHandler.Sharable
public class StatusHandler extends ChannelInboundHandlerAdapter {

    private int count = 0;

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        synchronized (this) {
            count++;
        }
        System.out.println("count:" + count);
    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("InBoundHandler1-channelRead:" + msg.toString());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

结果如下:

Connected to the target VM, address: '127.0.0.1:58772', transport: 'socket' count:1 InBoundHandler1-channelRead:abc23 InBoundHandler1-channelRead:asd count:2 InBoundHandler1-channelRead:abc23 InBoundHandler1-channelRead:asd

results matching ""

    No results matching ""