博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

netty 共享handler

Posted on 2016-05-27 14:14  bw_0927  阅读(1337)  评论(0)    收藏  举报

http://seeallhearall.blogspot.com/2012/06/netty-tutorial-part-15-on-channel.html

http://www.blogjava.net/hankchen/archive/2012/04/08/373572.html

 

 

MemoryAwareThreadPoolExecutor executor = new OrderedMemoryAwareThreadPoolExecutor(threadNums, maxChannelMemorySize,
                                                                                                        maxTotalMemorySize, keepAliveTime,
                                                                                                        TimeUnit.SECONDS);
ExecutionHandler executionHandler = new ExecutionHandler(executor);

public ChannelPipeline getPipeline() throws Exception
{
        ChannelPipeline pipeline = pipeline();
        pipeline.addLast("decoder", new AuthDecoder());
        pipeline.addLast("encoder", new AuthEncoder());
        pipeline.addLast("executor", executionHandler);    //这就是一个共享的handler,自此之后的hander将由executor线程池进行,不再是网络IO线程
        pipeline.addLast("handler", new AuthServerHandler(commandFactory));
        return pipeline;
}

 

 

 

There is one pipeline created per connection, but the pipeline may contain both shared and exclusive handlers. Some handlers do not keep state and a single instance can be inserted into multiple [all] pipelines. Netty provided handlers that can be shared are annotated with ChannelHandler.Sharable. See the section titled Shared and Exclusive Channel Handlers in this tutorial.

 

@override
是java document的标识

 

 

@Override是伪代码,表示重写(当然不写也可以),不过还是建议你写上,有如下好处:
    1>可以当注释用,方便阅读
    2>编译器可以给你验证@Override下面的方法名是否是你父类中所有的,如果没有则报错
      比如你如果没写@Override而你下面的方法名又写错了,这时你的编译器是可以通过的(它以为这个方法是你的
子类中自己增加的方法)
    

 

=======================================

final JLabel label;
class MeaningOfLifeFinder extends SwingWorker<String, Object> {
@Override
public String doInBackground() {
return findTheMeaningOfLife();
}
请教高手,@Override是什么意思,放在这里有什么用?

没什么用,这是你的IDE编辑器为你自动加上去的一个标志,告诉你说
下面这个方法是从父类/接口 继承过来的,需要你重写一次
这样就可以方便你阅读,也不怕会忘记


@Override是Java5的元数据
如果你的doInBackground并不是真的重写了父类方法就会编译失败


https://github.com/xuyue531/myBolg/blob/master/source/_posts/%E8%AE%B0Netty%E4%B8%AD%E5%85%B3%E4%BA%8Ehandler%E7%AE%A1%E7%90%86%E7%9A%84%E4%B8%80%E4%B8%AA%E9%97%AE%E9%A2%98.md


问题

用Netty写了一个服务器,发现在第二次连接的时候出现了以下错误。

分析

错误说明我的Decoder handler不是一个sharable handler,所以不能被多次添加和删除。于是定位到该handler添加的地方——ChannelInitializer类:

public class RFIDChannelInitializer extends ChannelInitializer<SocketChannel>{

    private static final RFIDServerHandler SERVER_HANDLER = new RFIDServerHandler();
    private static final Decoder DECODER = new Decoder();

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

//      pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
//      pipeline.addLast("bytesDecoder", new ByteArrayDecoder());
//      pipeline.addLast(new LineBasedFrameDecoder(100));
        pipeline.addLast("decoder", DECODER);    //是不对的
        pipeline.addLast("serverHandler", SERVER_HANDLER);     //共享同一个服务器逻辑处理handler
    }
}

看到DECODER是一个static final成员,所以在多个connection连接的时候都使用的同一个decoder,而decoder又没有申明为@sharable,所以就出现了上面的问题
一个channelHandler经常需要保存一些状态信息,最简单的就是使用成员变量。由于一个handler实例会包含一些特定connection的状态变量,故我们应该为每一个connection创建一个属于它的handler实例,来避免产生竞争条件
Netty4增加了sharable注解,使得handler能够被添加到多个ChannelPipeline中,一个handler可以产生多个ChannelHandlerContext,使得对应的handler可以在不同的channel中使用,此时应该注意线程安全。怎么是不安全的使用?看下面代码:

@Sharable  
public class NotSharableHandler extends ChannelInboundHandlerAdapter {  

    private int count;  

    @Override  
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {  
        count++;  
        System.out.println("channelRead(...) called the " + count + " time�");  
        ctx.fireChannelRead(msg);  
    }    
}  

上面是一个带@Sharable注解的Handler,它被多个线程使用时,里面count是不安全的,会导致count值错误。为什么要共享ChannelHandler?使用@Sharable注解共享一个ChannelHandler在一些需求中还是有很好的作用的,如使用一个ChannelHandler来统计连接数或来处理一些全局数据等等。

解决

那么我现在只要在我的Decoder类上加一个@sharable注释就可以了,但发现eclipse加不上去,手动import之后运行仍然有错误。
我的Decoder继承了ByteToMessageDecoder,而在其官方文档上明确的说明了:

Be aware that sub-classes of {@link ByteToMessageDecoder} MUST NOT annotated with {@link @Sharable}.

So,这个ByteToMessageDecoder及其子类都不能使用@Sharable注释。所以我需要为每一个connection创建独立的decoder handler实例。

public class RFIDChannelInitializer extends ChannelInitializer<SocketChannel>{

    private static final RFIDServerHandler SERVER_HANDLER = new RFIDServerHandler();
//  private static final Decoder DECODER = new Decoder();
//  private static final StringDecoder DECODER = new StringDecoder();

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

//      pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
//      pipeline.addLast("bytesDecoder", new ByteArrayDecoder());
//      pipeline.addLast(new LineBasedFrameDecoder(100));
        pipeline.addLast("decoder", new Decoder());
        pipeline.addLast("serverHandler", SERVER_HANDLER);

    }

}

"is not a @Sharable handler "

当出现这样的错误提示时,有两种解决方法,要么就是加上注解@Sharable,要么就是每次都new一个新的handler的实例。

另外,在断开连接时仍然会调用decode函数,而此时的Bytebuf是空的(EmptyByteBuf),而在out.add(e)时,如果e为null,则会抱空指针异常,所以在out.add(e)之前要判断e是否为null。

protected void decode(ChannelHandlerContext ctx, ByteBuf in,
            List<Object> out) throws Exception {
        String hexString = ByteBufUtil.hexDump(in);
        in.clear();
        Message message = analyzehis(hexString);
        if (message != null) {
            out.add(message);
        }
    }

参考

[1] Netty In Action中文版 - 第六章:ChannelHandler
[1] netty4_is not a @Sharable handler
[3] 玩转Netty – 从Netty3升级到Netty4 [4] Netty 4.x源码