初始化
对于Client
的初始化过程,本章主要会介绍怎样初始化ChannelPipeline
以及与Server
建立连接。Server
初始化过程比Client
端复杂一些,它分为两部分,分为Acceptor
的处理链初始化过程和Reactor
链的初始化过程。绑定端口提供服务的过程。
Client
首先来看Client
的初始化代码:
1 | // 配置事件组,即线程组,会在构造函数中创建线程,默认情况下他会创建 CPU数*2个 线程 |
Bootstrap类是Client
端的启动辅助类。
整个过程中,分为两大步,第一步进行参数设置,设置Bootstrap的参数,第二步则是发起连接。
在Bootstrap参数设置里,可以看到业务方配置了group
(客户端事件线程池EventLoopGroup
,后面单领出来讲解)、Channel
的类型(比如对于NIO
和Epoll
Channel)和 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线程的处理链:
服务端到此就完成了初始化过程。