NioEventLoop再次学习

gitgub:NioEventLoop再学习

昨天开始学习了NioEventLoop,今天详细看了NioEventLoop的底层实现。

NioEventLoop的继承关系如图所示:

从图中可以看出来,NioEventLoop的终极父类是Executor,也就是是说NioEventLoop是一个线程池(但是它也可以看做一个线程)。在程序中并没有使用NioEventLoop,而是通过使用NioEventLoopGroup对象来最终使用NioEventLoop的。

1 NioEventLoopGroup

NioEventLoopGroup是一个线程池,从上面的类继承关系可以看出来,NioEventLoopGroup的终极父类也是Executor,因为EventLoopGroup是它的父类。NioEventLoopGroup作为一个线程池,里面盛放的对象就是NioEventLoop对象。也就是说NioEventLoopGroup中盛放的时一个一个的线程池NioEventLoop。下面看看NioEventLoopGroup底层实现。

通过new NioEventLoopGroup()会调用NioEventLoopGroup的构造器,在一步一步,最终调用NioEventLoopGroup的父类的构造器,这里说一点,既然NioEventLoopGroup是一个线程池,那么就会指定线程池中的线程数,如果直接new NioEventLoopGroup()那么会开启默认个线程,默认数是CPU内核的2倍。

会调用父类MultithreadEventLoopGroup的构造器,这里会指定NioEventLoopGroup池中的线程数
protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) {super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args);
}//调用父类MultithreadEventExecutorGroup的构造器
protected MultithreadEventExecutorGroup(int nThreads, ThreadFactory threadFactory, Object... args) {//线程工厂,一定是用来创建线程的threadFactory = this.newDefaultThreadFactory();//这里会发现NioEventLoopGroup中实际上存放的就是EventExecutor,把它看做线程//以数组的形式保存this.children = new SingleThreadEventExecutor[nThreads];//实例化每个数组元素,也就是创建每一个EventExecutor对象this.children[i] = this.newChild(threadFactory, args);//一旦数组中有EventExecutor创建不成功,那么本次创建失败//已经创建的EventExecutor对象就要被销毁this.children[j].shutdownGracefully();
}

这里需要看看newChild()方法:

//可以发现这里会交由其子类来重写
protected abstract EventExecutor newChild(ThreadFactory var1, Object... var2) throws Exception;

因为关注的是NioEventLoopGroup,因此看看它对该方法的重写:

protected EventExecutor newChild(ThreadFactory threadFactory, Object... args) throws Exception {return new NioEventLoop(this, threadFactory, (SelectorProvider)args[0], ((SelectStrategyFactory)args[1]).newSelectStrategy(), (RejectedExecutionHandler)args[2]);
}

从这个方法可以看出,创建的EventExecutor实际上就是NioEventLoop对象。

从这里就可以看出,虽然程序中没有直接操作NioEventLoop,但是通过NioEventLoopGroup,就可以使用NioEventLoop。

2 NioEventLoop

直接看上面调用的NioEventLoop的构造方法:

 NioEventLoop(NioEventLoopGroup parent, ThreadFactory threadFactory, SelectorProvider selectorProvider, SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {//首先调用父类构造器super(parent, threadFactory, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);if (selectorProvider == null) {throw new NullPointerException("selectorProvider");} else if (strategy == null) {throw new NullPointerException("selectStrategy");} else {this.provider = selectorProvider;//这个和获得Selector对象的方法很像,估计就是NioEventLoop.SelectorTuple selectorTuple = this.openSelector();this.selector = selectorTuple.selector;this.unwrappedSelector = selectorTuple.unwrappedSelector;this.selectStrategy = strategy;}
}

然后调用了NioEventLoop的父类构造器:

//继续调用父类构造器
protected SingleThreadEventLoop(EventLoopGroup parent, ThreadFactory threadFactory, boolean addTaskWakesUp, int maxPendingTasks, RejectedExecutionHandler rejectedExecutionHandler) {super(parent, threadFactory, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
}//调用SingleThreadEventExecutor构造器
protected SingleThreadEventExecutor(EventExecutorGroup parent, ThreadFactory threadFactory, boolean addTaskWakesUp, int maxPendingTasks, RejectedExecutionHandler rejectedHandler) {//创建SingleThreadEventExecutor线程池中的线程this.thread = threadFactory.newThread(new Runnable() {public void run() {调用run方法SingleThreadEventExecutor.this.run();}}}//由子类重写
protected abstract void run();

解释:SingleThreadEventExecutor是一个线程池,因此需要制定里面的线程,从名字可以看出来,SingleThreadEventExecutor线程池中只有一个线程,也就是它的thread属性。SingleThreadEventExecutor的子类NioEventLoop又重写了run()方法,因此在SingleThreadEventExecutor线程池中的线程执行的时候回调用NioEventLoop的run()方法。然后把创建的NioEventLoop对象赋值给MultithreadEventExecutorGroup中的children[i].

至此可以明白了,创建NioEventLoopGroup时,实际上就创建了若干个NioEventLoop线程池对象,并将其放入到NioEventLoopGroup中,交由其统一管理。

又因为NioEventLoop现场池中只有一个线程,因此,可以把NioEventLoop看做一个线程。NioEventLoop负责的事情就是连接和IO操作。具体就是在NioEventLoop的run方法中实现的。

3 NioEventLoop的职责

创建完NioEventLoopGroup之后,实际上就创建了NioEventLoop,然后将NioEventLoopGroup绑定到启动类上Bootstrap,服务器端是绑定到ServerBootstrap启动类上。

//绑定
bootstrap.group(group,worker);//启动,监控,在这里会启动NioEventLoop中的线程,监听,或者处理IO
bootstrap.bind()//同步等待.sync();

看看bind()方法,然后进行一系列的调用:

public ChannelFuture bind() {    return this.doBind(localAddress);
}//doBind
private ChannelFuture doBind(final SocketAddress localAddress) {AbstractBootstrap.doBind0(regFuture, channel, localAddress, promise);
}//doBind0
private static void doBind0(final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) {/***这里会执行eventLoop().execute(),往线程池中提交任务*eventLoop()会返回NioEventLoop对象*那么就会执行NioEventLoop的execute()方法*/channel.eventLoop().execute(new Runnable() {public void run() {if (regFuture.isSuccess()) {channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);} else {promise.setFailure(regFuture.cause());}}});
}//NioEventLoop从SingleThreadEventExecutor继承过来的execute()
public void execute(Runnable task) {//这里启动NioEventLoop中的线程this.startThread();
}

然后将会执行run()方法,NioEventLoop的重写的run方法也会被执行:

//NioEventLoop重写的runprotected void run() {while(true) {while(true) {//实际调用select方法阻塞,等待IO或者网络连接this.select(this.wakenUp.getAndSet(false));//处理相关事件   this.processSelectedKeys();}}
}

下面就是经过一系列调用执行的方法:

private void processSelectedKeysPlain(Set<SelectionKey> selectedKeys) {//这就和利用原生java NIO jdk编程非常像了,获得channel上发生动作的集合,遍历这个集合,//不停的处理这些channel上的动作if (!selectedKeys.isEmpty()) {Iterator i = selectedKeys.iterator();while(true) {SelectionKey k = (SelectionKey)i.next();Object a = k.attachment();i.remove();if (a instanceof AbstractNioChannel) {this.processSelectedKey(k, (AbstractNioChannel)a);} else {NioTask<SelectableChannel> task = (NioTask)a;processSelectedKey(k, task);}if (!i.hasNext()) {break;}if (this.needsToSelectAgain) {this.selectAgain();selectedKeys = this.selector.selectedKeys();if (selectedKeys.isEmpty()) {break;}i = selectedKeys.iterator();}}}
}

ok,至此完毕。可算是NioEventLoop搞懂了。

4 总结

在程序编写的时候不需要直接操作NioEventLoop,而是通过NioEventLoopGroup来,间接创建NioEventLoop。NioEventLoopGroup是一个线程池,里面存放若干个NioEventLoop对象。

NioEventLoop是一个线程池对象,但是它里面只有一个线程,因此也可以将其看做一个线程,它主要做的事情就是,开启它里面的唯一的那个线程,如果是负责监听连接的话,那么将ServerSocketChannel注册在这个线程上,并声明对Accept感兴趣,这个线程就会通过Selector调用select()阻塞,直至有连接请求过来,那么就开始处理;如果是负责IO的话,那么将SocketChannel注册在这个线程上,并声明对Read/Write感兴趣,这个线程也会通过Selector调用select()阻塞,直至有IO操作发生,然后唤醒处理IO。

5 参考资料

Netty源码分析之服务启动: https://www.jianshu.com/p/e577803f0fb8

netty(三) NioEventLoop再学习相关推荐

  1. 【通知】有三AI带学习资料的固态硬盘最后5个

    还记得今年年中时发布的有三AI定制版固态硬盘吗,年前最后一期还有5个,2个大号,3个小号,先到先得,下面是硬盘相关的具体细节介绍. 硬盘产品细节 本次我们的产品为固态移动硬盘,注意是固态硬盘,不是普通 ...

  2. 【杂谈】有三AI专业版学习扑克牌上线,一副扑克,看懂AI核心技术

    相信现在大部分小伙伴只要有可能都躲在家里,外面有病毒和酷暑,那怎样才能学习娱乐两不误呢?是时候该我们出手了,有三AI专业版学习扑克牌今日奉上! 一副扑克,看懂AI核心技术 目前AI技术在各行各业中都大 ...

  3. RTSP再学习 -- 利用FFmpeg 将 rtsp 获取H264裸流并保存到文件中

    如需转载请注明出处:https://blog.csdn.net/qq_29350001/article/details/78214267 既然已经可以通过 RTSP 获取h264 裸流了.那么通过 F ...

  4. LIVE555再学习 -- testOnDemandRTSPServer 源码分析

    一.简介 先看一下官网上的介绍: testOnDemandRTSPServercreates a RTSP server that can stream, via RTP unicast, from ...

  5. LIVE555再学习 -- testH264VideoStreamer 源码分析

    上一篇文章我们已经讲了一部分: testH264VideoStreamer 重复从 H.264 基本流视频文件(名为"test.264")中读取,并使用 RTP 多播进行流式传输. ...

  6. LIVE555再学习 -- testRTSPClient 实例

    上一篇文章简单看了一遍 testRTSPClient  的源码,接下来举几个应用实例加深一下. 首先什么都不做修改,先执行一遍,看一下. 一.执行 testRTSPClient 特么,上面的东西我没看 ...

  7. LIVE555再学习 -- DM368/Hi3516A 交叉编译

    接着上篇文章来讲,参看:LIVE555再学习 -- Linux 下编译 下载源码.文件介绍部分不再介绍.主要看配置编译部分. 三.配置编译 DM368 的交叉编译器为 arm-none-linux-g ...

  8. MQTT再学习 -- 交叉编译与移植

    先说明一下,遇到的问题.我之前在 Ubuntu12.04 gcc 下可以搭建 mqtt 服务器生成的 libmosquitto.so.1. 参看:MQTT再学习 -- 搭建MQTT服务器及测试 现在我 ...

  9. MQTT再学习 -- 安装MQTT客户端及测试

    上一篇文章我们已经讲了 MQTT 服务器的搭建,参看:MQTT再学习 -- 搭建MQTT服务器及测试 接下来我们看一下 MQTT 客户端. 一.客户端下载 首先,客户端也有多种,我们需要面临选择了. ...

最新文章

  1. python 调试 cyberbrain 教程
  2. 潘建伟团队首次实现18个光量子比特纠缠,刷新世界记录
  3. multi task训练torch_手把手教你使用PyTorch(2)-requires_gradamp;computation graph
  4. (转)超全面设计指南:如何做大屏数据可视化设计?
  5. 如何用python做俄罗斯方块_你的童年有俄罗斯方块吗?教你用 Python 实现俄罗斯方块!...
  6. Halcon教程三:了解基础算子
  7. css3 简单的动画实现欢乐愉快的小鱼
  8. heur.riskfm.bbx病毒解决方案--www.baoluowanxiang.com
  9. 纯CSS实现圆角阴影的折角效果
  10. 深度学习【道路提取】:马萨诸塞州道路数据集分享
  11. 阿里云团队漏洞托管、渗透测试、攻防演练
  12. linux内核2.6.3x--Network device support
  13. PHP 简单汉字繁简数据转换
  14. XSS Challenges闯关1-6
  15. vue 使用人脸识别_使用Vue.js和Kairos构建简单的人脸识别应用
  16. C语言快速排序(郝斌老师笔记)
  17. 【Cesium】使用TLE轨道两行数计算轨道信息,并生成CZML格式文件
  18. 上周技术关注:Google是如何运行的?
  19. 【codevs1975】化学方程式 dfs
  20. 6款不错的jquery画图插件

热门文章

  1. 计算机科学与技术论文选题怎么选,比较好写的计算机科学与技术专业论文选题 计算机科学与技术专业论文题目如何取...
  2. 分享过几个【贪吃蛇】了,再分享一下也不过分吧?(妙趣横生)
  3. java习题小分享【关于statis你知道多少】
  4. 502粘到手上变硬了怎么办_急手被502胶水粘住了怎么办?
  5. 游戏图片文件和声音文件的隐藏
  6. 拼多多产品怎么引流?拼多多商品怎么引更多的流量?
  7. ABAP 使用Smartforms发送HTML邮件
  8. 网页制作和java有关系么_网页设计注意问题
  9. JQury实现ajax异步请求
  10. 什么是tv域名?.tv域名不能实名吗?