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

时间: 2024-08-06 00:48:05

Netty源码学习(五)ChannelInitializer的相关文章

Netty源码学习——Included transports(传输方式)

Transport API的核心: Channel接口 类图表示Channel含有Pipeline和Config接口,pipeline上一节有所介绍. Channel是线程安全的,这表示在多线环境下操作同一个Channel,不会有数据问题. final Channel channel = null; final ByteBuf buf = Unpooled.copiedBuffer("your data", CharsetUtil.UTF_8); // #1 Runnable writ

Netty源码学习——ChannelPipeline模型分析

参考Netty API io.netty.channel.ChannelPipeline A list of ChannelHandlers which handles or intercepts inbound events and outbount operations of aChannel.ChannelPipeline implements an advanced form of theIntercepting Filter pattern to give a user full co

Netty源码学习——EventLoopGroup原理:NioEventLoopGroup分析

类结构图: 不了解Executor接口原理的可以查看concurrent包中的api介绍,这里只介绍Netty中EventExecutorGroup的主要功能! 从类的结构图中可以看到EventExecutorGroup是直接继承ScheduledExecutorService这个接口的,为了说明白Group的原理这里顺便提一下ScheduledExecutorService的用途! java.util.concurrent.ScheduledExecutorService An Executo

Netty 源码(五)NioEventLoop

Netty 源码(五)NioEventLoop Netty 基于事件驱动模型,使用不同的事件来通知我们状态的改变或者操作状态的改变.它定义了在整个连接的生命周期里当有事件发生的时候处理的核心抽象. Channel 为 Netty 网络操作抽象类,EventLoop 主要是为 Channel 处理 I/O 操作,两者配合参与 I/O 操作. 下图是 Channel.EventLoop.Thread.EventLoopGroup 之间的关系. 一个 EventLoopGroup 包含一个或多个 Ev

Netty源码学习(六)ChannelPipeline

0. ChannelPipeline简介 ChannelPipeline = Channel + Pipeline,也就是说首先它与Channel绑定,然后它是起到类似于管道的作用:字节流在ChannelPipeline上流动,流动的过程中被ChannelHandler修饰,最终输出. 1. ChannelPipeline类图 ChannelPipeline只有两个子类,直接一起放上来好了,其中EmbeddedChannelPipeline主要用于测试,本文只介绍DefaultChannelPi

NETTY源码学习-DELIMITERBASEDFRAMEDECODER

看DelimiterBasedFrameDecoder的API,有举例: 接收到的ChannelBuffer如下: +--------------+ | ABC\nDEF\r\n | +--------------+ 经过DelimiterBasedFrameDecoder(Delimiters.lineDelimiter())之后,得到: +-----+-----+ | ABC | DEF | +-----+-----+ 而不是 +----------+ | ABC\nDEF | 为什么 ?

(三)Netty源码学习笔记之boss线程处理流程

尊重原创,转载注明出处,原文地址:http://www.cnblogs.com/cishengchongyan/p/6160194.html  本文我们将先从NioEventLoop开始来学习服务端的处理流程.话不多说,开始学习~~~~ 我们从上文中已经知道server在启动的时候会开启两个线程:bossGroup和workerGroup,这两个线程分别是boss线程池(用于接收client请求)和worker线程池(用于处理具体的读写操作),这两个线程调度器都是NioEventLoopGrou

Netty源码学习(二)NioEventLoopGroup

0. NioEventLoopGroup简介 NioEventLoopGroup可以理解为一个线程池,内部维护了一组线程,每个线程负责处理多个Channel上的事件,而一个Channel只对应于一个线程,这样可以回避多线程下的数据同步问题. 1. NioEventLoopGroup类图 2. 构造方法 new NioEventLoopGroup()方法会调用到MultithreadEventLoopGroup的构造方法: private static final int DEFAULT_EVEN

Spring源码学习(五)bean的加载

加油加油 ?? bean加载的大致过程 1 /** 2 * Return an instance, which may be shared or independent, of the specified bean. 3 * 4 * @param name the name of the bean to retrieve 5 * @param requiredType the required type of the bean to retrieve 6 * @param args argume