Netty源码三:NioEventLoop创建与run方法

2024-01-31 20:20

本文主要是介绍Netty源码三:NioEventLoop创建与run方法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.入口

image.png
会调用到父类SingleThreadEventLoop的构造方法

2.SingleThreadEventLoop

image.png
继续调用父类SingleThreadEventExecutor的构造方法

3.SingleThreadEventExecutor

image.png
到这里完整的总结一下:

  1. 将线程执行器保存到每一个SingleThreadEventExcutor里面去
  2. 创建了MpscQueue,具体为什么,因为在NioEventLoop里面重写了newTaskQueue方法

image.png

  1. 等父类调用完毕,最后回到NioEventLoop里面,最重要的一件事:创建Selector

最后那一张图完整的总结一下:
image.png

4.NioEventLoop.run

4.1 调用入口

这里还是要回顾一下这个方法是什么时候调用的?
Netty在启动的时候,在调用config().group().register(channel) ,使用bossGroup做channel注册的时候,它会使用EventExecutorChooser找一个NioEventLoop然后去做注册,最终会调用到这个abstractChannel.register方法
image.png
一般来说,我们启动的时候,运行到这里,eventLoop会返回false,所以会调用到eventLoop.execute里面的方法,在这里我们就能找到这个NioEventLoop.run的调用地方
image.png
image.png
image.png
也就说在使用NioEventLoop将channel注册到selector的时候,会判断是不是eventloop线程调用,如果不是就会使用SingleThreadEventExecutor.execute执行,它会将NioEventLoop.run方法包装成一个runnable,然后创建一个线程并启动,然后就调用到NioEventLoop里面了

4.2 run方法


/*** todo select()                    检查是否有IO事件* todo ProcessorSelectedKeys()     处理IO事件* todo RunAllTask()                处理异步任务队列*/
@Override
protected void run() {for (; ; ) {try {// todo hasTasks() true代表 任务队列存在任务switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {case SelectStrategy.CONTINUE:continue;case SelectStrategy.SELECT:// todo 轮询IO事件, 等待事件的发生, 本方法下面的代码是处理接受到的感性趣的事件, 进入查看本方法select(wakenUp.getAndSet(false));// wakenUp.compareAndSet(false, true)' is always evaluated// before calling 'selector.wakeup()' to reduce the wake-up// overhead. (Selector.wakeup() is an expensive operation.)//// However, there is a race condition in this approach.// The race condition is triggered when 'wakenUp' is set to// true too early.//// 'wakenUp' is set to true too early if:// 1) Selector is waken up between 'wakenUp.set(false)' and//    'selector.select(...)'. (BAD)// 2) Selector is waken up between 'selector.select(...)' and//    'if (wakenUp.get()) { ... }'. (OK)//// In the first case, 'wakenUp' is set to true and the// following 'selector.select(...)' will wake up immediately.// Until 'wakenUp' is set to false again in the next round,// 'wakenUp.compareAndSet(false, true)' will fail, and therefore// any attempt to wake up the Selector will fail, too, causing// the following 'selector.select(...)' call to block// unnecessarily.//// To fix this problem, we wake up the selector again if wakenUp// is true immediately after selector.select(...).// It is inefficient in that it wakes up the selector for both// the first case (BAD - wake-up required) and the second case// (OK - no wake-up required).if (wakenUp.get()) {selector.wakeup();}// fall throughdefault:}cancelledKeys = 0;needsToSelectAgain = false;final int ioRatio = this.ioRatio;  // todo 默认50// todo  如果ioRatio==100 就调用第一个     processSelectedKeys();  否则就调用第二个if (ioRatio == 100) {try {// todo 处理 处理发生的感性趣的事件processSelectedKeys();} finally {// Ensure we always run tasks.// todo 用于处理 本 eventLoop外的线程 扔到taskQueue中的任务runAllTasks();}} else {// todo 因为ioRatio默认是50 , 所以来else// todo 记录下开始的时间final long ioStartTime = System.nanoTime();try {// todo 处理IO事件processSelectedKeys();} finally {// Ensure we always run tasks.// todo  根据处理IO事件耗时 ,控制 下面的runAllTasks执行任务不能超过 ioTime 时间final long ioTime = System.nanoTime() - ioStartTime;// todo 这里面有聚合任务的逻辑runAllTasks(ioTime * (100 - ioRatio) / ioRatio);}}} catch (Throwable t) {handleLoopException(t);}// Always handle shutdown even if the loop processing threw an exception.try {if (isShuttingDown()) {closeAll();if (confirmShutdown()) {return;}}} catch (Throwable t) {handleLoopException(t);}}}


这里注释其实说得挺清楚,最后总结一下:

  1. select(wakenUp.getAndSet(false)):轮训io事件发生,当timeout或者有事件会break
  2. processSelectedKeys:处理io事件
  3. 运行taskQueue里面的任务

4.3 processSelectedKeys

在真正执行的时候,最终会走到processSelectedKeysOptimized,netty对底层selectKeys容器进行过优化,用数组代替了keyset
image.png

这里我为了debug,使用telnet localhost 8899, 接着就会进入到processSelectedKeysOptimized中
image.png
在走进processSelectedKey方法之后,最终会走到unsafe.read方法
image.png
image.png

5.AbstractNioMessageChannel

image.png
接着会调用到子类NioServerSocketChannel的doReadMessage(readBuf), 调用完子类之后,会通过pipline.fireChannelRead, 意思就是对于server端来说可读了,但是注意这时候传的是子类中创建的NioSocketChannel,说白了给server端一个NioSocketChannel,就是要创建新连接了。最终会调用到ServerBootstrapAcceptor的ChannelRead,具体看后面的ServerBootStrapAcceptor类
image.png

6.NioServerSocketChannel

image.png
这里主要做了几件事:

  1. SocketUtils.accpet接受连接,并创建jdk底层的socketChannel
  2. new NioSocketChannel:将jdk封装成NioSocketChannel,这里和创建NioServerSocketChannel的时候一样,不断的调用父类,同时创建NioServerChannel的pipline,!!!这里注意,这里会设置感兴趣的事件为read

image.png

7.ServerBootstrap#ServerBootstrapAcceptor

ServerBootstrapAcceptor是ServerBootstrap的内部类
之前AbstractNioMessageChannel里面的image.png,最终会调用到ServerBootstrapAcceptor的channelRead方法
image.png
简单的总结一下做了几件事:

  1. 给sockeChannel(也是这里的child)设置childHandler
  2. 使用childGroup.register 来完成socketChannel到Selector的注册,这里和SocketServerChannel到Selector的注册逻辑是一样的,都是从EventLoopGroup中选一个EventLoop(里面有Selector),然后调用jdk的register(eventloop.getSelector, 0, NioSocketChannel(netty对于socketChannel的包装))

8.AbstractChannel

childGroup.register -> MultithreadEventLoopGroup.register -> SingleThreadEventLoop.register -> AbstractChannel.register
image.png
image.png
AbstractChannel.abstractUnsafe.register
image.png
这一刻代码之前都讲过,就是把创建socketChannel注册到EventLoop上的Selector上去
AbstractChannel.register0():
image.png

  1. doRegister: 完成了SocketChannel到Selector的注册
  2. pipeline.invokeHandlerAddedIfNeeded: 这里会调用之前我们设置MyServerInitializer.initChannel(), 会往SocketChannel的pipeline中加入一系列读写用到的Handler

9.总结

最后那一张图总结一下:
image.png


这篇关于Netty源码三:NioEventLoop创建与run方法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/664954

相关文章

Nginx设置连接超时并进行测试的方法步骤

《Nginx设置连接超时并进行测试的方法步骤》在高并发场景下,如果客户端与服务器的连接长时间未响应,会占用大量的系统资源,影响其他正常请求的处理效率,为了解决这个问题,可以通过设置Nginx的连接... 目录设置连接超时目的操作步骤测试连接超时测试方法:总结:设置连接超时目的设置客户端与服务器之间的连接

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

C#中读取XML文件的四种常用方法

《C#中读取XML文件的四种常用方法》Xml是Internet环境中跨平台的,依赖于内容的技术,是当前处理结构化文档信息的有力工具,下面我们就来看看C#中读取XML文件的方法都有哪些吧... 目录XML简介格式C#读取XML文件方法使用XmlDocument使用XmlTextReader/XmlTextWr

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::