Netty服务端socket创建和线程池启动过程

一.类图和时序图

为便于理解,先上一幅UML 类图

 

下面我们分析服务端线程启动过程,先列出时序图,然后看着时序图来分析相关代码(大部分其它分析源码的文章都是上来就贴源码,根本就没有一个整体的概念,相信没有多少人愿意去看这样的分析文章

 

二 NioEventLoop的创建过程

写贴一段简单的服务端源码

  public static void main( String[] args )
    {
        EventLoopGroup boss = new NioEventLoopGroup(1);
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, worker).channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel channel) throws Exception {

                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            ChannelFuture ch = bootstrap.bind(14167).sync();
            ch.channel().closeFuture().sync();
        } catch (Exception e){
        }
        finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

 对照Reactor 模型, boss EventLoopGroup 即是负责客户端连接的线程池, worker EventLoopGroup 是负责IO 读写的线程池

NioEventLoop 创建的关键步骤

NioEventLoopGroup()
    ->MultithreadEventExecutorGroup()
        ->newChild() 
            -> NioEventLoop() 

以下是相关代码的截图

三   NioServerSocketChannel 的创建和初始化

3.1 NioServerSocketChannel 的创建

bootstrap.bind() 会调用doBind(), doBind() 是总的入口, doBind()  主要是做了两件事, 1.调用initAndResiter(), 2.阻塞自己,等待initAndRegister()完成,然后继续执行doBind0()

initAndRegister() 源码

第263 行会利用反射机制创建NioServerSocketChannel,

第105行 的eventloop 即是本文开头部分论述所创建的eventloop

至此NioServerSocketChannel 已经创建完成, 该channel 对应的pipleline 有两个ChannelHandleContext, 如下图所示

 3.2 init 过程

269行会调用init()函数,其核心代码如下

   void init(Channel channel) throws Exception {
   
        ChannelPipeline p = channel.pipeline();
    
    		...

        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(Channel ch) throws Exception {
                ch.pipeline().addLast(new ServerBootstrapAcceptor(currentChildHandler, currentChildOptions,
                        currentChildAttrs));
            }
        });
    }

该段代码主要是向channelPipleline 中增加一个ServerBootstrapAcceptor类型的handler,NioServerSocketChannel 对应 channelPipleline 将会有3个 ChannelHandleContext

 

四 线程池线程的启动和bind过程

接下来我们分析register()过程,也就是线程持线程的启动和bind过程

channel.unsafe.register() 会调用 AbstractUnsafe 类中的register 

在register 内部会调用是否在eventloop内部(调用线程和执行eventloop 的线程是否是同一个线程),在这里线程还尚未启动,因此会走到400行, 此时的eventloop 的 excute 方法为SingleThreadEventExecutor 的 excute 方法

在654行,调用startThread()启动线程,至此bossgroup 的NioEventLoop 线程正式启动启动起来,线程的run 方法将开始执行起来。

第655行addTask()将任务(即register0任务)放入线程的任务队列,

run 方法会从任务队列中逐个抽取任务,并执行对应的任务

// NioEventLoop.java
   @Override
    protected void run() {
        for (;;) {
     			....
     						
                runAllTasks(ioTime * (100 - ioRatio) / ioRatio);

 				...
            } catch (Throwable t) {
     
            }
        }
    }

我么在启动线程的时候,紧接着就放入了一个register0 任务,该任务会在这个启动的线程中执行, 下面我们分析下register0

424 行 会将创建的NioServerSocketChannel注册到Eventloop 上, 第426 行有一个 promise.setSuccess,  这里完成之后,AbstractBootstrap的doBind()函数结束阻塞,继续执行后续流程,即doBind0()

doBind0 会向eventLoop 的任务队列中投递一个bind 任务

309 行的bind 源码

189 行对应的源码

顺着366行继续往下走,会调用到 AbstractUnsafe 的bind

//AbstractUnsafe
 public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
     doBind()
}

在bind() 中会调用doBind() 函数,此doBind()  源码

 

以上分析了服务端socket的创建、线程池的启动、bind的主要过程,分析扣住了主要流程,忽略掉了一些细节,如对细节较感兴趣,可在这些框架基础之上继续增加这些细节过程

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值