Netty核心组件 ChannelPipeline和ChannelHandler与ChannelHandler的入站出站规则

本文围绕Netty展开,介绍了ChannelPipeline与Channel的对应关系,阐述了ChannelInitializer的添加及ChannelHandler的命名规则。重点讲解了Pipeline的事件传播机制,包括Inbound和Outbound的不同流向及传播方式,并通过测试验证。还提及了ChannelHandler与ChannelHandlerContext的关系、Channel的生命周期等知识。
概述

Netty中ChannelPipeline与Channel的对应关系是一一对应,也就是每个Channel中有且仅有一个ChannelPipeline,可以通过Channel获取唯一的ChannelPipeline,也可以通过ChannelPipeline获取唯一的Channel。

在这里插入图片描述

由上图可得,每个Channel维护了一个ChannelPipeline,而ChannelPipeline中又维护了一个由ChannelHandlerContext组成的双向链表,这个链表的表头是HeadContext,链表的末尾是TailContext,并且每个ChannelHanderContext中又关联了一个ChannelHandler。

HeadContext和TailContext并没有包含ChannelHandler,那是因为HeadContext和TailContext继承了AbstractChannelHandlerContext同时实现了ChannelHandler接口,所以它们有了Context和Handler双重属性。 就是HeadContext和TailContext以继承的形式实现这双重属性。

在这里插入图片描述
在这里插入图片描述

ChannelInitializer的添加

从上面我们知道,最开始的时候ChannelPipeline含有两个ChannelHandlerContext(HeadContext、TailContext),但是这个Pipeline不能实现什么特殊功能,因为我们还没有给他添加自定义的ChannelHandler,通常来说,我们在初始化Bootstrap时,会添加我们自定义的ChannelHandler。

实际上添加到Pipeline里面的是ChannelHandlerContext,只是每个对象维护了一个ChannelHandler。

Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
   
   
@Override
protected void initChannel(SocketChannel ch) throws Exception {
   
   
ChannelPipeline pipeline = ch.pipeline();
//这里添加自定义的。
pipeline.addLast(new ChatClientHandler(nickName));
}
});

在调用handler方法时,传入了ChannelInitializer对象,它提供了一个initChannel方法给我们进行重写,来进行初始化ChannelHandler,addLast表示在pipeline中的链表的TailContext之前添加一个ChannelHandler,用于自定义的功能实现。

在这里插入图片描述
这个是ChannelInitializer的类图,可以看出,他仅实现了ChannelInboundHandler接口。

在这里插入图片描述
上图是DefaultHandlerContext类的内容,可以看到有两个方法isInbound和isOutbound方法,这两个方法的逻辑是:当一个 handler 实现了 ChannelInboundHandler 接口,则 isInbound 返回 true;类似地,当一个handler 实现了 ChannelOutboundHandler 接口,则 isOutbound 就返回 true。而这两个 boolean 变量会传递到父类AbstractChannelHandlerContext 中,并初始化父类的两个字段:inbound 与 outbound。

并根据上面的ChannelInitializer的类关系图可得,ChannelInitializer的Inbound属性是true,Outbound属性是false。

addLast源码:

private void addLast0(AbstractChannelHandlerContext newCtx) {
   
   
//定位到TailContext的上一个Context,如果是第一次添加,那就是HeadContext
AbstractChannelHandlerContext prev = tail.prev;
//假如是第一次添加,那么把要加入的ChannelContext的前置节点赋值为HeadContext
newCtx.prev = prev;
//把要加入的ChannelContext的后置节点赋值为TailContext
newCtx.next = tail;
//如果是第一次添加,把HeadContext的后置节点赋值为新节点
prev.next = newCtx;
//把Tail节点的前置节点赋值为新结点。
tail.prev = newCtx;

//总的来说就是新加入的ChannelContext会在尾部加入,尾部指的是TailContext之前的节点,因为Pipeline的HeadContext和TailCOntext的位置总是不变的,一个在表头,一个在表尾。
}

自定义Handler的添加过程:按照上面代码,把注释去掉。

  1. 通过hand方法定义一个ChannelInitializer Handler到Pipeline中。此时的图如下:
    在这里插入图片描述
  2. 然后执行ChannelInitializer的initChannel方法,添加自定义的ChannelHandler,添加一个MyNettyChannelHandler。
    在这里插入图片描述
    就是在TailContext前添加,保证TailContext是最后的节点。
  3. 如果继续添加以此类推。
  4. 最后,initChannel方法执行完成添加之后,会在finally代码块里把ChannelInitializer自身删除。

在这里插入图片描述
上面方法是ChannelInitializer的方法。

所以最后的双向链表了只有HeadContext 之后是一些在initChannel方法中添加的自定义Channel,最后是TailContext,ChannelInitializer会被删除。

ChannelHandler的命名规则:

ChannelPipeline.addXXX都有一个重载版本:
比如

ChannelPipeline addLast(String name, ChannelHandler handler);

第一个参数指定添加的 handler 的名字(更准确地说是 ChannelHandlerContext 的名字,说成 handler 的名字更便于理解)。

public final ChannelPipeline addLast(String name, ChannelHandler handler) {
   
   
        return this.addLast((EventExecutorGroup)null, name, handler);
    }

    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
   
   
        final AbstractChannelHandlerContext newCtx;
        synchronized(this) {
   
   
        	//检查
            checkMultiplicity(handler);
            //会通过这个filterName方法进行handler名字的检查或者生成名字,如果名字重复,就会抛出异常,具体在下面
            newCtx = this.newContext(group, this.filterName(name, handler), handler);
            this.addLast0(newCtx);
            if (!this.registered) {
   
   
                newCtx.setAddPending();
                this.callHandlerCallbackLater(newCtx, true);
                return this;
            }

            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
   
   
                newCtx.setAddPending();
                executor.execute(new Runnable() {
   
   
                    public void run() {
   
   
                        DefaultChannelPipeline.this.callHandlerAdded0(newCtx);
                    }
                });
                return this;
            }
        }

        this.callHandlerAdded0(newCtx);
        return this;
    }

private String filterName(String name, ChannelHandler handler) {
   
   
        if (name == null) {
   
   
        //如果没有传递handler的名字,就使用默认命名规则
            return this.generateName(handler);
        } else {
   
   
        	//否则就进行检查。
            this.checkDuplicateName(name);
            return name;
        }
    }

//名字重复检查
 private void checkDuplicateName(String name) {
   
   
        if (this.context0(name) != null) {
   
   
            throw new IllegalArgumentException("Duplicate handler name: " + name);
        }
    }

//默认的命名规则:默认命名的规则很简单,就是用反射获取 handler 的 simpleName 加上"#0",代码就不解释了,源码在这里。
private String generateName(ChannelHandler handler) {
   
   
        Map<Class<?>, String> cache = (Map)nameCaches.get();
        Class<?> handlerType = han
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值