初始化
对于Client的初始化过程,本章主要会介绍怎样初始化ChannelPipeline以及与Server建立连接。Server初始化过程比Client端复杂一些,它分为两部分,分为Acceptor的处理链初始化过程和Reactor链的初始化过程。绑定端口提供服务的过程。
Client
首先来看Client的初始化代码:
1 | // 配置事件组,即线程组,会在构造函数中创建线程,默认情况下他会创建 CPU数*2个 线程 |
Bootstrap类是Client端的启动辅助类。
整个过程中,分为两大步,第一步进行参数设置,设置Bootstrap的参数,第二步则是发起连接。
在Bootstrap参数设置里,可以看到业务方配置了group(客户端事件线程池EventLoopGroup,后面单领出来讲解)、Channel的类型(比如对于NIO和EpollChannel)和 ChannelHandler(这里的ChannelInitializer 是给业务放提供的入口,让业务将自定义的ChannelHandler编制进Pipeline)。
发起连接: 利用设置的参数进行初始化处理链,注册事件、开启事件线程、发起连接的过程:
1 | Bootstrap.connect |
执行InitAndRegister方法主要是初始化Channel并与某个EventLoop绑定。执行doResolveAndConnect0方法用于解析地址并创建连接。下面就来分别详细解析这两个方法:
1 | final ChannelFuture initAndRegister() { |
register方法最终会调用MultithreadEventLoopGroup.register方法,而其只是按轮询的方式选择一个SingleThreadEventLoop执行register方法。
1 | public ChannelFuture MultithreadEventExecutorGroup.register(Channel channel) { |
可以看到register方法最终会调用Channel里的Unsafe(Unsafe是操作底层)的register方法:
1 | public final void register(EventLoop eventLoop, final ChannelPromise promise) { |
这里首先给Channel赋一个EventLoop,因为是启动线程不是该EventLoop,所以将register操作丢给该EventLoop执行。
在SingleThreadEventLoop.execute方法里:
可以看到当前线程没启动的时候会开启该线程,并将任务丢进EventLoop的任务队列中,EventLoop线程执行时从任务队列中获取任务执行。
1 | public void execute(Runnable task) { |
执行regsiter0方法:
1 | private void register0(ChannelPromise promise) { |
完成初始化处理链后,EchoClient的处理链如下:
完成注册后,则开始创建连接(Bootstrap.doConnect),
1 | private static void Bootstrap.doConnect( |
可以看到最终是会执行Channel.connect方法:
1 | public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) { |
Channel.connect方法则开始执行处理链,可以看到其从TailContext往前执行,执行ChannelOutboundHandler.connect方法,最终会执行HeadContext.connect方法。而其调用Unsafe.connect方法进行连接。最终调用原生SocketChannel.connect方法进行连接,连接成功后设置Promise状态通知监听者并执行pipeline.fireChannelActive。
Pipeline.fireChannelActive是由HeadContext往前传递的,其会触发EchoClientHandler的channelActive方法,然后再会触发readIfAutoRead方法,将NioSocketChannel中的selectionKey设置为SelectionKey.OP_READ(对于ServerSocketChannel则设置为OP_ACCEPT),以监听网络读事件。
1 | HeadContext->EchoClientHandler: channelActive |
到此Client初始化的过程就讲完了,下面我们将来讲解服务端内容。
Server
还是以Echo为例来讲解此过程:
1 | // acceptor事件组 |
ServerBootstrap 是Server端的辅助启动类。用户使用的时候会配置 Acceptor和Reactor的事件线程组(boosGroup的线程个数和监听端口数相等)。设置ServerSocketChannel类型,设置参数Channel的参数option,然后在Acceptor的处理链上中添加Handler。最后childHandler配置Reactor处理链,再其上添加业务方定制的Handler。
bind方法最终执行的是doBind方法:
1 | private ChannelFuture doBind(final SocketAddress localAddress) { |
在讲Client的初始化过程中,解释了initAndRegister的作用,实例化SocektChannel,并将其与一个EventLoop进行绑定,并将其注册到EventLoop的Selector上。这里,对于ServerSocketChannel会有所不同:init方法会调用ServerBootStrap的init方法:
1 | void init(Channel channel) { |
其主要是在 ServerSocketChannel 的处理链上加上添加 ChannelHandler,包括业务方配置的 LoggingHandler 和 ServerBootstrapAcceptor(Acceptor,负责接收请求,然后把请求丢给Reactor线程)。
初始化完成后,boss线程的处理链:
服务端到此就完成了初始化过程。