解决netty重复添加channel handler失败及其带来的反思

在使用Netty开发时遇到断线重连时添加自定义channel handler失败的问题,原因是非共享handler不能被重复添加。@Sharable注解标识可共享handler,框架通过此注解检查防止线程安全问题。Netty的线程模型确保单个channel与一个event loop绑定,多channel共享handler需确保线程安全。

前言
使用netty作为nio框架开发遇到了一个问题,断线重连时添加自定义channel handler失败,抛出异常

ChannelPiplineException:"xxx class is not a @Sharable handler, so can't be added or removed multiple times

字面意思上是我们自定义的channel handler不是一个共享的handler,不能被重复添加多次。那什么是可共享的handler呢?为什么pipline不允许添加不可共享的handler?解决问题的最好方式就是阅读源码。

可共享的handler
首先找到异常抛出的地点,ChannelPipline类中的addLast,一系列私有方法的调用(涉及其他的检查与处理但我们这里不需要关心)最终调用到addLast0这个方法:

 private void addLast0(final String name, DefaultChannelHandlerContext newCtx) {
        checkMultiplicity(newCtx);

        DefaultChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;

        name2ctx.put(name, newCtx);

        callHandlerAdded(newCtx);
}

可以看到方法开头就是对handler重复添加的检查,点进去看一下:

private static void checkMultiplicity(ChannelHandlerContext ctx) {
    ChannelHandler handler = ctx.handler();
    if (handler instanceof ChannelHandlerAdapter) {
        ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler;
        if (!h.isSharable() && h.added) {
            throw new ChannelPipelineException(
                    h.getClass().getName() +
                    " is not a @Sharable handler, so can't be added or removed multiple times.");
        }
        h.added = true;
    }
}

果然在这个方法里检查了handler是否是可共享的,判断逻辑就是看这个handler有没有被@Sharable这个接口所注解

public boolean isSharable() {
    return getClass().isAnnotationPresent(Sharable.class);
}

@Sharable注解

@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Sharable {
    // no value
}

注意@Inherited这个元注解,这样被@Sharable注解的抽象handler的具体实现类也具备被共享的能力啦。

为什么要设置可共享检查
了解了什么是可共享handler和怎么设置可共享handler,接下来的问题是为什么框架作者要煞费苦心的做这么一层检查呢?如果滥用@Sharable接口又会有哪些问题?这首先得从netty的线程模型说起。

netty中负责通信的channel始终只与一个event loop(可以抽象的看为一个处理线程)绑定,所以在handler非共享的情况下,同一个channel上的通信永远是线程安全的。这种线程模型让我们不用考虑多线程带来的安全隐患极大的减少了开发难度,当多个channel共享同一个handler时,如果handler持有的竞态资源如果没有考虑线程安全,很可能带来许多严重且难以排查的问题,因此我们在开发时应该遵循netty框架设计者的思路,尽量减少可共享handler的使用,如果必须使用,也必须在保证线程安全的情况下进行handler的共享。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值