Netty3之NioServerSocketChannelFactory

开门见山

之前的文章说了想要了解Netty服务端的秘密,就得先挖掘NioServerSocketChannelFactory到底做了些什么事情。ServerBootstrap绑定过程使用ChannelFactory创建Channel来完成绑定的一部分。

话不多说,下面我来看下NioServerSocketChannelFactory的创建过程。

NioServerSocketChannelFactory构造过程:

  1. 初始化两个Pool,一个BossPool,一个WorkerPool,默认使用NioServerBossPool和NioWorkerPool;
  2. 初始化NioServerSocketPipelineSink

看完BossPool和WorkerPool之后,你就会发现一旦NioServerSocketChannelFactory被构造之后,就启动了BossPool和WorkerPool里面的Boss/Worker线程了。

一个简单的new操作,就启动了Boss和Worker。

BossPool是什么

BossPool是维护Boss的一个容器,负责维护每个Boss对象的生命周期,包括创建、关闭。通过nextBoss()方法返回池子里的一个Boss对象。

AbstractNioBossPool是抽象父类,内部使用一个数组来充当容器,有两个子类,NioServerBossPool、NioClientBossPool。

NioServerBossPool构造过程:

  1. 传入Executor线程池,bossCount(作为BoosPool的容器大小,默认是1个),设置ThreadNameDeterminer(线程命名接口)
  2. 调用父类AbstractNioBossPool构造函数,设置相应的属性,创建Boss数组
  3. init()方法初始化
    1. 循环boss数组,依次调用newBoss()方法创建Boss对象
    2. waitForBossThreads ,等待每个boss线程运行,等待分析todo
  4. 回到NioServerBossPool,实现newBoss()方法,调用NioServerBoss构造函数创建对象

NioServerBoss继承AbstractNioSelector,是一个Boss接口。

NioServerBoss构造过程:

  1. 传入Executor线程池,ThreadNameDeterminer(线程命名接口)
  2. 调用父类AbstractNioSelector的构造器,设置Executor属性,这个Executor由NioServerBossPool传入
  3. openSelector
    1. open一个原生的NIO Selector
    2. 调用newThreadRenamingRunnable() 创建线程,丢入Executor中执行
  4. 回到NioServerBoss,实现newThreadRenamingRunnable()方法,就是使用本身创建ThreadRenamingRunnable

总结

调用了NioServerBossPool构造函数后,后续都发生了什么事情:创建bossCount个数量的Boss对象,每个Boss对象都打开一个Selector,并将自身丢入Executor线程池中执行,(这个Executor是由BossPool构造的时候传入的,因此每个Boss对象都共用同一个Executor)。在说的简单点,new NioServerBossPool()就是执行Boss线程。

new NioServerSocketChannelFactory()    -------->    new NioServerBoosPool()    --------->    Boss线程运行

WorkerPool是什么

知道了BossPool,让我们看看WorkerPool,先不看源码猜测下应该和BossPool逻辑都是一样的。

WorkerPool是维护Worker的一个容器,负责维护每个Worker对象的生命周期,包括创建、关闭。通过nextWorker()方法返回池子里的一个Worker对象。

AbstractNioWorkerPool是抽象父类,内部使用一个数组来充当容器,有两个子类,NioWorkerPool、NioDatagramWorkerPool(UDP协议)。

和Boss一样Worker接口也继承Runnable接口,Worker有两类实现,分别是NIO和OIO:

  • AbstractNioWorker,两个子类NioWorker、NioDatagramWorker
  • AbstractOioWorker,两个子类OioWorker、OioDatagramWorker

NioWorkerPool构造过程:

  1. 传入Executor线程池,workerCount(作为WorkerPool的容器大小,默认是CPU * 2),设置ThreadNameDeterminer(线程命名接口)
  2. 调用父类AbstractNioWorkerPool构造函数,设置相应的属性,创建Worker数组
  3. init()方法初始化
    1. 循环worker数组,依次调用newWorker()方法创建Worker对象
    2. waitForWorkerThreads ,等待每个worker线程运行,等待分析todo
  4. 回到NioWorkerPool,实现newWorker()方法,调用NioWorker构造函数创建对象

NioWorker继承AbstractNioWorker,是一个Worker接口。

NioWorker构造过程:

  1. 传入Executor线程池,ThreadNameDeterminer(线程命名接口)
  2. 调用父类AbstractNioWorker的构造器,AbstractNioWorker继承AbstractNioSelector
  3. 调用父类AbstractNioSelector的构造器,设置Executor属性,这个Executor由NioWorkerPool传入
  4. openSelector
    1. open一个原生的NIO Selector
    2. 调用newThreadRenamingRunnable() 创建线程,丢入Executor中执行
  5. 回到NioWorker,实现newThreadRenamingRunnable()方法,就是使用本身创建ThreadRenamingRunnable

总结

调用了NioWorkerPool构造函数后,后续都发生了什么事情:创建workerCount个数量的Worker对象,每个Worker对象都打开一个Selector,并将自身丢入Executor线程池中执行,(这个Executor是由WorkerPool构造的时候传入的,因此每个Worker对象都共用同一个Executor)。在说的简单点new NioWorkerPool()就是执行Worker线程。

new NioServerSocketChannelFactory()    -------->    new NioWorkerPool()    --------->    Worker线程运行

Boos和Worker流程是一样的

上面的分析乍一看,BoosPool和WorkerPool构造的流程都是一样的,都是创建N个Boss/Worker,一旦创建就丢入线程池中运行。需要注意的是Worker同样也打开了Selector。那么Boss和Worker两者的区别是什么呢,那就是两者的run()方法的逻辑了。

生产NioServerSocketChannel来做什么

NioServerSocketChannel构造过程:创建NIO的ServerSocketChannel,触发一个OPEN的Upstream Event,结束,就这样简单。

那么怎么启动服务端呢,那就是调用NIOServerSocketChannel的bind()方法(不管是在ServerBootstrap的Binder类还是主动去调用)。让我们看下Channel的bind()方法做了什么,其实很简单Channel的bind()方法是触发一个BOUND的Downstream Event。

顺便一提Channel的一些列主动的方法,比如bind、connect等都是触发一个相同类型的Downstream Event,在Pipeline中流转完之后,最终交给ChannelSink去处理。

所有可以这么认为,Channel的一系列动词的方法,就直接去看对应的ChannelSink的处理逻辑。

NioServerSocketChannel对应的是NioServerSocketPipelineSink,NioServerSocketPipelineSink里处理的BOUND的Event交给了Boss对象的bind方法。那NioServerBoss的bind做了些什么,生成了一个RegisterTask放到boss的任务队列里等待线程执行。这个RegisterTask是要做什么,是使用NIO ServerSocketChannel来执行bind逻辑,然后注册到Selector中。

总结一下

Channel的bind    -------->    ChannelSink    ------>    NioServerBoss.bind    ------>    RegisterTask ------->    NIO的绑定和注册逻辑

另外

ThreadRenamingRunnable

ThreadRenamingRunnable本身实现Runnable,它接收一个Runnable对象,在run方法中会将线程名重命名,执行Runnable对象的run方法,完了之后重置线程名。线程名重命名交给ThreadNameDeterminer接口。因此可以简单的认为ThreadRenamingRunnable等同于它的Runnable属性。

DeadLockProofWorker

start(final Executor parent, final Runnable runnable),使用parent去执行runnable,这个类是做什么的?为什么这么设计?

转载于:https://my.oschina.net/cregu/blog/2874472

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值