• Netty源码学习(五)ChannelInitializer


    0. ChannelInitializer简介

    直接用ChannelInitializer的注释吧:A special ChannelInboundHandler which offers an easy way to initialize a Channel once it was registered to its eventLoop.

    1. ChannelInitializer类图

    需要注意的是:

    a. ChannelInitializer继承于ChannelInboundHandler接口

    b. ChannelInitializer是一个抽象类,不能直接使用

    2. initChannel抽象方法

    ChannelInitializer中声明了一个名为initChannel的抽象方法:

    /**
    * 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;

    ChannelInitializer的实现类必须要重写这个方法,这个方法在Channel被注册到EventLoop的时候会被调用

    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);
            }
        }

    大概意思是:

    a. 触发ChannelInitializer的initChannel方法,执行子类定义的一系列操作(在ServerBootstrap这个例子中就是将ServerBootstrapAcceptor注册到pipeline中)

    b. 将ChannelInitializer从pipeline中移除

    4. 总结

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

    使用场景:

    a. 在ServerBootstrap初始化时,为监听端口accept事件的Channel添加ServerBootstrapAcceptor

    b. 在有新链接进入时,为监听客户端read/write事件的Channel添加用户自定义的ChannelHandler

  • 相关阅读:
    iOS 中使用 XIB 自定义cell的两种方法以及编译出现常见 的错误 (xcode6.0之后)
    iOS中 学会如何对sqlite3 进行封装
    杭电ACM2012素数判定
    杭电ACM2503a/b+c/d
    杭电ACM2005第几天
    杭电ACM2034人见人爱AB
    杭电ACM2502月之数
    杭电ACM2001计算两点间的距离
    杭电ACM2002计算求得体积
    杭电ACM2033人见人爱A+B
  • 原文地址:https://www.cnblogs.com/stevenczp/p/7597903.html
Copyright © 2020-2023  润新知