netty理论之源码分析⑥ 细讲ChannelInitializer

1.ChannelInitializer是啥

一种特殊的ChannelInboundHandler,它提供了在通道注册到eventLoop后初始化通道的简单方法,ChannelInitializer的主要目的是为程序员提供了一个简单的工具,用于在某个Channel注册到EventLoop后,对这个Channel执行一些初始化操作。ChannelInitializer虽然会在一开始会被注册到Channel相关的pipeline里,但是在初始化完成之后,ChannelInitializer会将自己从pipeline中移除,不会影响后续的操作。

使用场景:

  • 在ServerBootstrap初始化时,为监听端口accept事件的Channel添加ServerBootstrapAcceptor
  • 在有新链接进入时,为监听客户端read/write事件的Channel添加用户自定义的ChannelHandler

1.1、ChannelInitializer类图

由图可以看出 ChannelInitializer继承于ChannelInboundHandler接口;ChannelInitializer是一个抽象类,不能直接使用。

2. initChannel抽象方法

ChannelInitializer中声明了一个名为initChannel的抽象方法,ChannelInitializer的实现类必须要重写这个方法,这个方法在Channel被注册到EventLoop的时候会被调用:

/**
 * This method will be called once the {@link Channel} was registered. After the method returns this instance
 * will be removed from the {@link ChannelPipeline} of the {@link Channel}.
 *
 * @param ch            the {@link Channel} which was registered.
 * @throws Exception    is thrown if an error occurs. In that case it will be handled by
 *                      {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close
 *                      the {@link Channel}.
 */
protected abstract void initChannel(C ch) throws Exception;

3. ChannelInitializer什么时候会被调用?

以ServerBootstrap启动这一场景为例

在ServerBootstrap.init()方法中,负责accept新链接的Channel的pipeline被添加了一个ChannelInitializer

p.addLast(new ChannelInitializer<Channel>() {
    @Override
    public void initChannel(final Channel ch) throws Exception {
        final ChannelPipeline pipeline = ch.pipeline();
        ChannelHandler handler = config.handler();
        if (handler != null) {
            pipeline.addLast(handler);
        }

        ch.eventLoop().execute(new Runnable() {
            @Override
            public void run() {
                pipeline.addLast(new ServerBootstrapAcceptor(
                        ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
            }
        });
    }
});

由于此时这个Channel还没有被register到EventLoop,于是在addLast方法的调用链中,会给pipeline添加一个PendingHandlerAddedTask,其目的是在Channel被register到EventLoop的时候,触发一个回调事件

然后在AbstractBootstrap.initAndRegister()方法中,这个Channel会被register到boss EventLoopGoup,接着会被register到boss EventLoopGoup中的某一个具体的EventLoop

在AbstractChannel.register0()方法中,之前注册的PendingHandlerAddedTask会被调用,经过一系列调用之后,ChannelInitializer.handleAdded()方法会被触发:

/**
     * {@inheritDoc} If override this method ensure you call super!
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        if (ctx.channel().isRegistered()) {
            // This should always be true with our current DefaultChannelPipeline implementation.
            // The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering
            // surprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers
            // will be added in the expected order.
            initChannel(ctx);
        }
    }

    @SuppressWarnings("unchecked")
    private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
        if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
            try {
                initChannel((C) ctx.channel());//调用子类重写的initChannel方法
            } catch (Throwable cause) {
                // Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
                // We do so to prevent multiple calls to initChannel(...).
                exceptionCaught(ctx, cause);
            } finally {
                remove(ctx);//将ChannelInitializer从pipeline中移除
            }
            return true;
        }
        return false;
    }


    /**
     * This method will be called once the {@link Channel} was registered. After the method returns this instance
     * will be removed from the {@link ChannelPipeline} of the {@link Channel}.
     *
     * @param ch            the {@link Channel} which was registered.
     * @throws Exception    is thrown if an error occurs. In that case it will be handled by
     *                      {@link #exceptionCaught(ChannelHandlerContext, Throwable)} which will by default close
     *                      the {@link Channel}.
     */
    protected abstract void initChannel(C ch) throws Exception;


    private void remove(ChannelHandlerContext ctx) {
        try {
            ChannelPipeline pipeline = ctx.pipeline();
            if (pipeline.context(this) != null) {
                pipeline.remove(this);
            }
        } finally {
            initMap.remove(ctx);
        }
    }

上面代码大致意思是:触发ChannelInitializer的initChannel方法,执行子类定义的一系列操作(在ServerBootstrap这个例子中就是将ServerBootstrapAcceptor注册到pipeline中);将ChannelInitializer从pipeline中移除。

总结:ChannelInitializer的用法,也就是要自己定义initChannel函数,当当前的channel被注册到eventloop上面之后,会用该用户定义的函数来初始化channel,一般都是一些加入handler的操作

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值