1. Netty 源码分析之启动BootStrap

Bootstrap 是 Netty 提供的一个便利的工厂类,我们可以通过它来完成 Netty 的客户端或服务器端的 Netty 初始化。

1. 客户端 Bootstrp

Bootstrap 是 Netty 提供的一个便利的工厂类,我们可以通过它来完成 Netty 的客户端或服务器端的 Netty 初始化。下面一个客户端初始化的例子:

EventLoopGroup group = new NioEventLoopGroup();
try {
    Bootstrap bootstrap = new Bootstrap();
    bootstrap.group(group).channel(NioSocketChannel.class)
        .option(ChannelOption.TCP_NODELAY, true)
        .handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel channel) throws Exception {
                channel.pipeline().addLast(new ProtobufVarint32FrameDecoder());
                ...
                channel.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
                channel.pipeline().addLast(new ProtobufEncoder());
                channel.pipeline().addLast(new SubReqClientHandler());
            }
        });
    ChannelFuture f = bootstrap.connect(host, port).sync();
    f.channel().closeFuture().sync();
} finally {
	group.shutdownGracefully();
}

例子中,Netty初始化共做了以下几种任务:

  • 指定一个线程池,例子中的线程池类型为 NioEventLoopGroup,表示是一个NIO的线程池
  • 指定 Channel 类型,这里是客户端,所以用了 NioSocketChannel
  • 设置数据的处理器,例子中为 SubReqClientHandler

1.1 group 的初始化

例子中,实例化了一个 NioEventLoopGroup 对象,从代码中追踪一下 EventLoop 的初始化过程,源码中发现 NioEventLoopGroup 有几个重载的构造器,不过内容都没有什么大的区别,最终都是调用的父类 MultithreadEventLoopGroup 构造器:

protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
    super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args);
}
private static final int DEFAULT_EVENT_LOOP_THREADS =
    Math.max(1,
    SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

Netty 会首先从系统属性中获取 "io.netty.eventLoopThreads"的值, 如果我们没有设置它的话, 那么就返回默认值:NettyRuntime.availableProcessors() * 2,即:两倍的处理器核心数。最后调用父类 MultithreadEventExecutorGroup的构造器,处理逻辑代码如下:

protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) {
    ...
        // 如果执行器为空时,会初始化一个线程执行器
        if (executor == null) {
            executor = new ThreadPerTaskExecutor(this.newDefaultThreadFactory());
        }

        this.children = new EventExecutor[nThreads];
        ...
        for(int i = 0; i < nThreads; ++i) {
            ...
            this.children[i] = this.newChild((Executor)executor, args);
            ...
        }

        this.chooser = chooserFactory.newChooser(this.children);
        ...
    }
}

根据代码,就很清楚 MultithreadEventExecutorGroup 中的都做了哪些工作:

  • 创建一个大小为 nThreads 的 SingleThreadEventExecutor数组
  • 调用 newChild方法初始化 children 数组,这个是一个抽象方法,在 NioEventLoopGroup类中有实现
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
    return new NioEventLoop(this, executor, (SelectorProvider)args[0], ((SelectStrategyFactory)args[1]).newSelectStrategy(), (RejectedExecutionHandler)args[2]);
}
  • 根据 nThreads 的大小,创建不同的 Chooser, 从 children 数组中选出一个合适的 EventExecutor实例。
    public EventExecutorChooser newChooser(EventExecutor[] executors) {
        return (EventExecutorChooser)(isPowerOfTwo(executors.length) ?
            new DefaultEventExecutorChooserFactory.PowerOfTwoEventExecutorChooser(executors)
                : new DefaultEventExecutorChooserFactory.GenericEventExecutorChooser(executors));
    }

其中,this.newDefaultThreadFactory()的任务是创建一个线程工厂,参数设置了线程名、是否守护线程、优先级以及是否属于安全管理器的线程组等,默认是不是守护线程、优先级是5。另外,ThreadPerTaskExecutor其实是一个工厂类,主要管理线程工厂,真正执行的是线程工厂创建和启动线程的。

public DefaultThreadFactory(String poolName, boolean daemon, int priority) {
    this(poolName, daemon, priority, System.getSecurityManager() == null ?
        Thread.currentThread().getThreadGroup() : System.getSecurityManager().getThreadGroup());
}

分析得到,MultithreadEventExecutorGroup内部维护了一个 EventExecutor数组,Netty的 EventLoopGroup的实现机制其实就建立在 MultithreadEventExecutorGroup之上,每当 Netty 需要一个 EventLoop时,会调用 next()方法获取一个可用的 EventLoop

总结一下整个 EventLoopGroup 的初始化过程:

  • EventLoopGroup 内部维护一个类型为EventExecutor children数组,其大小是 nThreads,构成了一个线程池,默认值是两倍处理器核心数
  • EventLoopGroup 中会调用 newChild抽象方法来初始化 children数组,它返回一个 NioEventLoop实例,其中两个比较重要的属性:
    SelectorProvider provider // NioEventLoopGroup 构造器中通过SelectorProvider.provider() 获取一个 SelectorProvider
    Selector selector // NioEventLoop 构造器中通过调用 selector = provider.openSelector() 获取一个 selector 对象

1.2 channel 的初始化

在 Netty 中,Channel 是一个 Socket 的抽象,它为用户提供了关于 Socket 状态(是否是连接还是断开) 以及对 Socket 的读写等操作. 每当 Netty 建立了一个连接后,都会有一个对应的 Channel 实例。
具体看一下channel的初始化过程,主要做了以下任务:

  • ChannelFactory 和 Channel 类型的确定
  • Channel 的实例化
  • unsafe 字段的初始化
  • pipeline 的初始化

1.2.1 ChannelFactory 和 Channel 类型的确定

除了 TCP 协议以外,Netty 还支持很多其他的连接协议,并且每种协议还有 NIO(非阻塞 IO)
和 OIO(Old-IO,即传统的阻塞 IO) 版本的区别. 不同协议不同的阻塞类型的连接都有不同的。

Channel 类型与之对应下面是一些常用的 Channel 类型:

  • NioSocketChannel,代表异步的客户端 TCP Socket 连接
  • NioDatagramChannel,异步的 UDP 连接
  • NioSctpChannel,异步的客户端 Sctp 连接
  • NioSctpServerChannel,异步的 Sctp 服务器端连接
  • OioSocketChannel,同步的客户端 TCP Socket 连接
  • OioServerSocketChannel,同步的服务器端 TCP Socket 连接
  • OioDatagramChannel,同步的 UDP 连接
  • OioSctpChannel,同步的 Sctp 服务器端连接
  • OioSctpServerChannel,同步的客户端 TCP Socket 连接

例子中,bootstrap.group(group).channel(NioSocketChannel.class)channel 这个方法其实就是初始化了一个 ReflectiveChannelFactory 反射工厂类,实现了 ChannelFactory 接口,通过反射:

public B channel(Class<? extends C> channelClass) {
    if (channelClass == null) {
        throw new NullPointerException("channelClass");
    } else {
        return this.channelFactory((io.netty.channel.ChannelFactory)(new ReflectiveChannelFactory(channelClass)));
    }
}
public class ReflectiveChannelFactory<T extends Channel> implements ChannelFactory<T> {
    private final Class<? extends T> clazz;

    public ReflectiveChannelFactory(Class<? extends T> clazz) {
        if (clazz == null) {
            throw new NullPointerException("clazz");
        } else {
            this.clazz = clazz;
        }
    }

    public T newChannel() {
        try {
            return (Channel)this.clazz.getConstructor().newInstance();
        } catch (Throwable var2) {
            throw new ChannelException("Unable to create Channel from class " + this.clazz, var2);
        }
    }

    public String toString() {
        return StringUtil.simpleClassName(this.clazz) + ".class";
    }
}

1.2.2 Channel 的实例化

那么 ChannelFactory.newChannel()方法在哪里调用呢?
继续跟踪发现其调用链是:

Bootstrap.connect -> Bootstrap.doResolveAndConnect -> AbstractBootstrap.initAndRegister

AbstractBootstrap.initAndRegister中调用了 channelFactory().newChannel()来获取一个新的 NioSocketChannel 实例,其源码如下:

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        channel = this.channelFactory.newChannel();
        this.init(channel);
    } catch (Throwable var3) {
        ...
    }
    ...
    return regFuture;
}

反射工厂 ReflectiveChannelFactory 中,this.clazz.getConstructor().newInstance() ,调用类的默认构造器实例化对象,默认构造器如下:

public class NioSocketChannel extends AbstractNioByteChannel implements SocketChannel {
    ...
    private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
    private final SocketChannelConfig config;
    private static java.nio.channels.SocketChannel newSocket(SelectorProvider provider) {
        try {
            return provider.openSocketChannel();
        } catch (IOException var2) {
            throw new ChannelException("Failed to open a socket.", var2);
        }
    }

    public NioSocketChannel() {
        this(DEFAULT_SELECTOR_PROVIDER);
    }

    public NioSocketChannel(SelectorProvider provider) {
        this(newSocket(provider));
    }

    public NioSocketChannel(java.nio.channels.SocketChannel socket) {
        this((Channel)null, socket);
    }

    public NioSocketChannel(Channel parent, java.nio.channels.SocketChannel socket) {
        super(parent, socket);
        this.config = new NioSocketChannel.NioSocketChannelConfig(this, socket.socket(), null);
    }
    ...
}

在此过程中,会创建一个 SocketChannel,从代码中可以看出创建的 channel 的 parent channel 为 null,调用父类 AbstractNioChannel、AbstractChannel的构造函数,其中 AbstractNioChannel构造方法中,设置了线程为非阻塞的 ch.configureBlocking(false)

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
    super(parent);
    this.ch = ch;
    this.readInterestOp = readInterestOp;

    try {
        ch.configureBlocking(false);
    } catch (IOException var7) {
        ...
    }
}
protected AbstractChannel(Channel parent) {
    this.parent = parent;
    this.id = this.newId();//唯一标识
    this.unsafe = this.newUnsafe();
    this.pipeline = this.newChannelPipeline();
}

总结一下 NioSocketChannel 实例化过程中,主要工作就是实例化了一个 SocketChannel。

1.2.3 unsafe 字段的初始化

上一小节中提到了,channel 初始化时,会对unsafe初始化,它的作用是什么呢?
其实 unsafe 特别关键,它封装了对 Java 底层 Socket 的操作,实际上是沟通 Netty 上层和 Java 底层的重要的桥梁。
Unsafe 接口所提供的方法,封装的是 Java 底层的 Socket 的操作,源码如下:

public interface Unsafe {
    Handle recvBufAllocHandle();

    SocketAddress localAddress();

    SocketAddress remoteAddress();

    void register(EventLoop var1, ChannelPromise var2);

    void bind(SocketAddress var1, ChannelPromise var2);

    void connect(SocketAddress var1, SocketAddress var2, ChannelPromise var3);

    void disconnect(ChannelPromise var1);

    void close(ChannelPromise var1);

    void closeForcibly();

    void deregister(ChannelPromise var1);

    void beginRead();

    void write(Object var1, ChannelPromise var2);

    void flush();

    ChannelPromise voidPromise();

    ChannelOutboundBuffer outboundBuffer();
}

其实 newUnsafe方法被 NioSocketChannel 重写了,实质上创建的是 NioSocketChannelUnsafe :

protected AbstractNioUnsafe newUnsafe() {
    return new NioSocketChannel.NioSocketChannelUnsafe(null);
}

1.2.4 pipeline 的初始化

channel 初始化时,还会对 ChannelPipeline 初始化,根据 Each channel has its own pipeline and it is created automatically when a new channel is created,我们知道,在实例化一个 Channel 时,必然伴随着实例化一个 ChannelPipeline。 而我们确实在 AbstractChannel 的构造器看到了 pipeline 字段被初始化为 DefaultChannelPipeline 的实例。 DefaultChannelPipeline 构造器做了哪些工作呢?

protected DefaultChannelPipeline(Channel channel) {
    this.channel = (Channel)ObjectUtil.checkNotNull(channel, "channel");
    this.succeededFuture = new SucceededChannelFuture(channel, (EventExecutor)null);
    this.voidPromise = new VoidChannelPromise(channel, true);
    this.tail = new DefaultChannelPipeline.TailContext(this);
    this.head = new DefaultChannelPipeline.HeadContext(this);
    this.head.next = this.tail;
    this.tail.prev = this.head;
}

在 DefaultChannelPipeline 中,还有两个特殊的字段,即 head 和 tail,它们是一个双向链表的头和尾, 其实在 DefaultChannelPipeline 中,维护了一个以 AbstractChannelHandlerContext 为节点的双向链表,这个链表是 Netty 实现 Pipeline 机制的关键。链表中 head 是一个实现了 ChannelOutboundHandler 和 ChannelInboundHandler 的类,但在父类构造器中,将 head 认为 ChannelOutboundHandler 初始化的,而 tail 则是一个 ChannelInboundHandler。

HeadContext(DefaultChannelPipeline pipeline) {
    super(pipeline, (EventExecutor)null, DefaultChannelPipeline.HEAD_NAME, false, true);
    this.unsafe = pipeline.channel().unsafe();
    this.setAddComplete();
}
TailContext(DefaultChannelPipeline pipeline) {
    super(pipeline, (EventExecutor)null, DefaultChannelPipeline.TAIL_NAME, true, false);
    this.setAddComplete();
}

1.2.5 channel 注册

在前面的分析中,channel 会在 Bootstrap.initAndRegister中进行初始化, 但是这个方法还会将初始化好的 Channel 注册到 EventGroup 中。接下来我们就来分析一下 Channel 注册的过程。

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        channel = this.channelFactory.newChannel();
        this.init(channel);
    }
    ...
    ChannelFuture regFuture = this.config().group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        } else {
            channel.unsafe().closeForcibly();
        }
    }
    return regFuture;
}

调用链路如下:

AbstractBootstrap.initAndRegister
    -> MultithreadEventLoopGroup.register
        -> SingleThreadEventLoop.register
            -> AbstractChannel$AbstractUnsafe.register
                -> AbstractChannel$AbstractUnsafe.register0
                   -> AbstractNioChannel.doRegister

注册的代码如下:

protected void doRegister() throws Exception {
    boolean selected = false;

    while(true) {
        try {
            this.selectionKey = this.javaChannel().register(this.eventLoop().unwrappedSelector(), 0, this);
            return;
        } catch (CancelledKeyException var3) {
            if (selected) {
                throw var3;
            }

            this.eventLoop().selectNow();
            selected = true;
        }
    }
}

javaChannel()这个方法在前面我们已经知道了,它返回的是一个 Java NIO SocketChannel,这里我们将这个 SocketChannel 注册到与 eventLoop 关联的 selector 上了
总结一下 Channel 的注册过:

  • 首先在 AbstractBootstrap.initAndRegister中,通过 group().register(channel),调用 MultithreadEventLoopGroup.register方法
  • MultithreadEventLoopGroup.register中 ,通过 next()获取一个可用的 SingleThreadEventLoop,然后调用它的 register
  • SingleThreadEventLoop.register中,通过 channel.unsafe().register(this, promise)来获取 channel 的 unsafe()底层操作对象,然后调用它的 register
  • AbstractUnsafe.register方法中,调用 register0方法注册 Channel
  • AbstractUnsafe.register0中,调用 AbstractNioChannel.doRegister方法
  • AbstractNioChannel.doRegister方法通过 javaChannel().register(eventLoop().selector, 0, this)将 Channel 对应的 Java NIO SockerChannel 注册到一个 eventLoop 的 Selector 中,并且将当前 Channel 作为 attachment

总的来说,Channel 注册过程所做的工作就是将 Channel 与对应的 EventLoop 关联,因此这也体现了,在 Netty 中,每个 Channel 都会关联一个特定的 EventLoop,并且这个 Channel 中的所有 IO 操作都是在这个 EventLoop 中执行的;当关联好 Channel 和 EventLoop 后,会继续调用底层的 Java NIO SocketChannel 的 register 方法,将底层的 Java NIO SocketChannel 注册到指定的 selector 中。

1.2.6 handler 的添加过程

Netty 的一个强大和灵活之处就是基于 Pipeline 的自定义 handler 机制。因此可以像添加插件一样自由组合各种各样的 handler 来完成业务逻辑。例如我们需要处理 HTTP 数据,那么就可以在 pipeline 前添加一个 Http 的编解码的 Handler,然后接着添加我们自己的业务逻辑的 handler,这样网络上的数据流就向通过一个管道一样,从不同的 handler 中流过并进行编解码,最终在到达我们自定义的 handler 中。
Bootstrap.handler方法接收一个 ChannelHandler,而我们传递的是一个派生于 ChannelInitializer的匿名类,它正好也实现了 ChannelHandler接口。
上一节中提到,Bootstrap.initAndRegister中对channel进行注册,函数中还处理了初始化工作,具体如下:

void init(Channel channel) throws Exception {
    ChannelPipeline p = channel.pipeline();
    p.addLast(new ChannelHandler[]{this.config.handler()});
    Map<ChannelOption<?>, Object> options = this.options0();
    synchronized(options) {
        setChannelOptions(channel, options, logger);
    }

    Map<AttributeKey<?>, Object> attrs = this.attrs0();
    synchronized(attrs) {
        Iterator var6 = attrs.entrySet().iterator();

        while(var6.hasNext()) {
            Entry<AttributeKey<?>, Object> e = (Entry)var6.next();
            channel.attr((AttributeKey)e.getKey()).set(e.getValue());
        }

    }
}

其中,p.addLast(new ChannelHandler[]{this.config.handler()})ChannelInitializer添加到了 ChannelPipeline中。
接下来看一下, ChannelInitializer 类内到底有什么玄机:

public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
    if (this.initChannel(ctx)) {
        ctx.pipeline().fireChannelRegistered();
    } else {
        ctx.fireChannelRegistered();
    }
}
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
    if (this.initMap.putIfAbsent(ctx, Boolean.TRUE) == null) {
        try {
            this.initChannel(ctx.channel());
        } catch (Throwable var6) {
            this.exceptionCaught(ctx, var6);
        } finally {
            this.remove(ctx);
        }

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

    }

发现channelRegistered方法中调用了channel初始化的工作,即添加 ChannelHandler 到 ChannelPipeline,完成后会移除 ChannelInitializer。其中, ctx.pipeline().fireChannelRegistered()添加注册事件。

1.2.7 connect 分析

private static void doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {
    final Channel channel = connectPromise.channel();
    channel.eventLoop().execute(new Runnable() {
        public void run() {
            if (localAddress == null) {
                channel.connect(remoteAddress, connectPromise);
            } else {
                channel.connect(remoteAddress, localAddress, connectPromise);
            }

            connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
        }
    });
}

这里调用的是 channel.connect(remoteAddress, connectPromise)的channel,就是NioSocketChannel,继承簇中,发现只有 AbstractChannel实现了该方法。

channel.connect(remoteAddress, connectPromise)
    -> AbstractChannel.connect(SocketAddress, ChannelPromise)
        -> DefaultChannelPipeline.connect(SocketAddress, ChannelPromise) // 这里是channel中的 tail
            -> AbstractChannelHandlerContext.connect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
                    ...
                    // tail 一直往前遍历,找到 outbound为ture,其实是head,它继承了ChannelOutboundHandler
                    final AbstractChannelHandlerContext next = this.findContextOutbound();
                    EventExecutor executor = next.executor();
                    if (executor.inEventLoop()) {
                        next.invokeConnect(remoteAddress, localAddress, promise);
                    } else {
                        safeExecute(executor, new Runnable() {
                            public void run() {
                                next.invokeConnect(remoteAddress, localAddress, promise);
                            }
                        }, promise, (Object)null);
                    }

                    return promise;
                }
    }
private void invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
    if (this.invokeHandler()) {
        try {
            ((ChannelOutboundHandler)this.handler()).connect(this, remoteAddress, localAddress, promise);
        } catch (Throwable var5) {
            notifyOutboundHandlerException(var5, promise);
        }
    } else {
        this.connect(remoteAddress, localAddress, promise);
    }
}

HeadContext 重写了 connect 方法,因此实际上调用的是 HeadContext.connect。 我们接着跟踪到 HeadContext.connect,其代码如下:

public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
    this.unsafe.connect(remoteAddress, localAddress, promise);
}

由继承关系得到,调用的地方是 AbstractNioChannel.AbstractNioUnsafe.connect,而他内部调用的是 AbstractNioChannel.this.doConnect(remoteAddress, localAddress),NioSocketChannel 实现了该方法:

protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
    if (localAddress != null) {
        this.doBind0(localAddress);
    }

    boolean success = false;
    boolean var5;
    try {
        boolean connected = SocketUtils.connect(this.javaChannel(), remoteAddress);
        if (!connected) {
            this.selectionKey().interestOps(8);
        }

        success = true;
        var5 = connected;
    } finally {
        if (!success) {
            this.doClose();
        }
    }
    return var5;
}

首先是获取 Java NIO SocketChannel,从 NioSocketChannel.newSocket 返回的 SocketChannel 对象,然后是调用 SocketChannel.connect 方法完成 Java NIO 层面上的 Socket 的连接。

2 服务端 Bootstrp

在分析客户端的代码时,我们已经对 Bootstrap 启动 Netty 有了一个大致的认识,那么接下来分析服务器端时,就会相对简单一些了。首先还是来看一下服务器端的启动代码:

EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
    ServerBootstrap bootstrap = new ServerBootstrap();
    bootstrap.group(bossGroup, workerGroup)
        .channel(NioServerSocketChannel.class)
        .option(ChannelOption.SO_BACKLOG, 100)
        .handler(new LoggingHandler(LogLevel.INFO))
        .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
                ...
                ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
                ch.pipeline().addLast(new ProtobufEncoder());
                ch.pipeline().addLast(new SubReqServerHandler());
            }
        });

    ChannelFuture future = bootstrap.bind(port).sync();
    future.channel().closeFuture().sync();
} finally {
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
}

相对于客户端,区别主要有以下几方面

  1. 线程池方面:使用了两个线程池EventLoopGroup,分别是bossGroupworkerGroup,此线程模型为主从模型,一个用于处理客户端请求,一个处理与各个客户端连接的 I/O 操作;其实,这里也可以指定一个线程池,初始化时,会将父子线程池设置成同一个;
  2. channel初始化方面:channel定义为 NioServerSocketChannel类型;
  3. handler初始化方面:区分出来 handlerchildHandler参数设置,主要用于分别设置父子线程池的handler。

2.1 bossGroup 与 workerGroup

bossGroup 是用于服务端的 accept 的,即用于处理客户端的连接请求。可以把 Netty 比作一个饭店, bossGroup 就像一个前台接待,当客户来到饭店吃时,接待员就会引导顾客就坐,为顾客端茶送水等。而 workerGroup,其实就是实际上干活的啦,它们负责客户端连接通道的 IO 操作:当接待员招待好顾客后,就可以稍做休息,而此时后厨里的厨师们(workerGroup)就开始忙碌地准备饭菜了。

final ChannelFuture initAndRegister() {
    Channel channel = null;

    try {
        channel = this.channelFactory.newChannel();
        this.init(channel);
    }
    ...
    ChannelFuture regFuture = this.config().group().register(channel);
    ...
    return regFuture;
}

代码中,this.config().group().register(channel)是对bossGroup 和 channel 进行关联,通过一层层的调用链,发现最终调用的是 AbstractUnsafe.register,源代码如下:

public final void register(EventLoop eventLoop, final ChannelPromise promise) {
    ...
        if (eventLoop.inEventLoop()) {
            this.register0(promise);
        } else {
            try {
                eventLoop.execute(new Runnable() {
                    public void run() {
                        AbstractUnsafe.this.register0(promise);
                    }
                });
            }
            ...
        }
    }
}

private void register0(ChannelPromise promise) {
    try {
        ...
        AbstractChannel.this.doRegister();
        ...
        AbstractChannel.this.pipeline.fireChannelRegistered();
        if (AbstractChannel.this.isActive()) {
            if (firstRegistration) {
                AbstractChannel.this.pipeline.fireChannelActive();
            } else if (AbstractChannel.this.config().isAutoRead()) {
                this.beginRead();
            }
        }
    }
    ...
}

发现channel 注册到 bossGroup时,调用了 pipeline.fireChannelRegistered()AbstractChannel.this.pipeline.fireChannelActive()方法,进行 channel 注册操作。

void init(Channel channel) throws Exception {

    ...
    ChannelPipeline p = channel.pipeline();
    final EventLoopGroup currentChildGroup = this.childGroup;
    final ChannelHandler currentChildHandler = this.childHandler;
    ...

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

            ch.eventLoop().execute(new Runnable() {
                public void run() {
                    pipeline.addLast(new ChannelHandler[]{new ServerBootstrap.ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)});
                }
            });
        }
    }});
}

从源代码中,发现new了一个 ServerBootstrapAcceptor,它重写了 channelRead

public void channelRead(ChannelHandlerContext ctx, Object msg) {
    final Channel child = (Channel)msg;
    ···
    try {
        this.childGroup.register(child).addListener(new ChannelFutureListener() {
            public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                    ServerBootstrap.ServerBootstrapAcceptor.forceClose(child, future.cause());
                }

            }
        });
    } catch (Throwable var8) {
        forceClose(child, var8);
    }

}

这里的 childGroup.register 就是将 workerGroup 中的某个 EventLoop 和 NioSocketChannel 关联了。 那么 ServerBootstrapAcceptor.channelRead方法是怎么被调用的呢?
其实当一个 client 连接到 server 时,Java 底层的 NIO ServerSocketChannel 会有一个 SelectionKey.OP_ACCEPT就绪事件,接着就会调用到 NioServerSocketChannel.doReadMessages

protected int doReadMessages(List<Object> buf) throws Exception {
    SocketChannel ch = SocketUtils.accept(this.javaChannel());
    try {
        if (ch != null) {
            buf.add(new NioSocketChannel(this, ch));
            return 1;
        }
    }
    ...
    return 0;
}

代码中,SocketUtils.accept(this.javaChannel())获取到客户端新连接的 SocketChannel,new NioSocketChannel(this, ch)创建了 NioSocketChannel,有客户端 Bootstrap 分析可知,这里是等待读操作,接下来就经由 Netty 的 ChannelPipeline 机制,将读取事件逐级发送到各个 handler 中,于是就会触发前面我们提到的 ServerBootstrapAcceptor.channelRead 方法了。

2.2 NioServerSocketChannel 的实例化过程

private static java.nio.channels.ServerSocketChannel newSocket(SelectorProvider provider) {
    try {
        return provider.openServerSocketChannel();
    } catch (IOException var2) {
        throw new ChannelException("Failed to open a server socket.", var2);
    }
}

public NioServerSocketChannel() {
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
public NioServerSocketChannel(java.nio.channels.ServerSocketChannel channel) {
    super((Channel)null, channel, 16);
    this.config = new NioServerSocketChannel.NioServerSocketChannelConfig(this, this.javaChannel().socket());
}
public static final int OP_READ = 1;
public static final int OP_WRITE = 4;
public static final int OP_CONNECT = 8;
public static final int OP_ACCEPT = 16;

和 NioSocketChannel 类似,构造器都是调用了 newSocket 来打开一个 Java 的 NIO Socket,不过需要注意的是,客户端的 newSocket 调用的是 openSocketChannel,而服务器端的 newSocket 调用的是 openServerSocketChannel。
在客户端的 Channel 初始化时,传入的参数是 SelectionKey.OP_READ,这里设置了 SelectionKey.OP_ACCEPT,即通知 selector 客户端的连接请求。
逐层调用初始化后,与客户端一样,会创建一个 unsafe 和 pipeline:

protected AbstractChannel(Channel parent) {
    this.parent = parent;
    this.id = this.newId();
    this.unsafe = this.newUnsafe();
    this.pipeline = this.newChannelPipeline();
}
protected AbstractNioUnsafe newUnsafe() {
    return new AbstractNioMessageChannel.NioMessageUnsafe();
}

但是,在服务器端 unsafe 字段其实是一个 AbstractNioMessageChannel.AbstractNioUnsafe 的实例 NioMessageUnsafe 。

2.3 Handler 的添加过程

void init(Channel channel) throws Exception {

    ...
    ChannelPipeline p = channel.pipeline();
    final EventLoopGroup currentChildGroup = this.childGroup;
    final ChannelHandler currentChildHandler = this.childHandler;
    ...

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

            ch.eventLoop().execute(new Runnable() {
                public void run() {
                    pipeline.addLast(new ChannelHandler[]{new ServerBootstrap.ServerBootstrapAcceptor(ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs)});
                }
            });
        }
    }});
}

从代码中看出,ChannelPipeline p = channel.pipeline()得到的是 NioServerSocketChannel对应的 pipeline,它添加了 handler 和 ServerBootstrapAcceptor,即 bossGroup 对应的 channel 添加了自定义初始化的 handler 以及 ServerBootstrapAcceptor。具体的流程是,当 channel 绑定到 eventLoop 后(在这里是
NioServerSocketChannel 绑定到 bossGroup) 中时,会在 pipeline 中发出 fireChannelRegistered 事件,接着就会触发 ChannelInitializer.initChannel 方法的调用,最终将 ServerBootstrapAcceptor 绑定到 pipeline 上。
上面小节中,workerGroup 与 新建的客户端 SockerChannel 绑定说到,ServerBootstrapAcceptor 重写了 channelRead方法,在这里对 channel 的 pipeline进行添加自定义的 childHandler。

public void channelRead(ChannelHandlerContext ctx, Object msg) {
    final Channel child = (Channel)msg;
    child.pipeline().addLast(new ChannelHandler[]{this.childHandler});
    ...
    try {
        this.childGroup.register(child).addListener(new ChannelFutureListener() {
            public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                    ServerBootstrap.ServerBootstrapAcceptor.forceClose(child, future.cause());
                }

            }
        });
    } catch (Throwable var8) {
        forceClose(child, var8);
    }

}

最后总结一下服务器端的 handler 与 childHandler 的区别与联系:

  1. 在服务器 NioServerSocketChannel 的 pipeline 中添加的是 handler 与 ServerBootstrapAcceptor。
  2. 当有新的客户端连接请求时,ServerBootstrapAcceptor.channelRead 中负责新建此连接的 NioSocketChannel 并添加 childHandler 到 NioSocketChannel 对应的 pipeline 中,并将此 channel 绑定到 workerGroup 中的某个 eventLoop 中。
  3. handler 是在 accept 阶段起作用,它处理客户端的连接请求。
  4. childHandler 是在客户端连接建立以后起作用,它负责客户端连接的 IO 交互。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值