性能文章>netty源码分析之nio线程个数以及线程命名规则>

netty源码分析之nio线程个数以及线程命名规则原创

5月前
289205

概述

netty是一个以高性能著称的网络通信框架,许多开源项目都使用了netty作为底层网络通信框架,如avro,dubbo,nats本文将从源码的角度讲述netty在确定线程个数方面如何保证应用程序性能最优,源码基于netty-4.1.6.Final

线程个数在何时达到最优

我们知道,在一个应用中,如果cpu计算的时间为Tcpu,io操作的时间为Tio,系统的cpu核数为Ncpu,线程个数为Nthread, 那么理论上线程个数满足Nthread = (1+Tio/Tcpu)*Ncpu,应用的性能达到最优

netty中线程逻辑

下面是一段标准的netty服务端代码

我们把重点放在

在不传递任何参数的时候,netty会默认给一个参数0

我们继续往下跟


到了这里已经调用到父类MultithreadEventLoopGroup


到了这里,我们基本可以暂停了,因为我们刚开始没有传参数,所以netty给我们默认传递了一个构造参数0,netty会判断线程个数是否为0,如果为0的话就是用默认的线程个数,而默认线程个数相关的代码如下

我们可以看到,如果没有设置程序启动参数,那么默认情况下线程的个数为cpu的核数乘以2
那么为什么netty要将worker的线程个数设置为2倍的cpu个数呢?按照第一小节的理论,如果线程个数设置为2倍的cpu线程个数,那么Tio/Tcpu的值就是1,也就是说在netty的nio线程中,cpu时间和io时间相等,我们继续跟进netty的源码,netty中每个nio线程要做的事在NioEventLooprun方法里面,有这么一段代码

我们跳到else分支,processSelectionKeys()要做的事情就是IO操作(select),netty在处理io之前记录了一下io操作的开始时间,然后在io结束的时候计算了一下这段io操作花的总的时间ioTime,然后runAllTask方法表示花多长时间来处理一下netty内部的任务队列(cpu计算为主),在这里,netty传递的参数为ioTime * (100 - ioRatio) / ioRatio)
为什么要传递这个参数?netty的目的是应用的性能达到最高,netty默认情况下开启了两倍cpu核数个线程,按照第一小节的理论,必须保证cpu时间和io时间相等,也就是Tio = Tcpu,即ioTime =  ioTime * (100 - ioRatio) / ioRatio)
所以ioRatio默认条件下应该是50,查看ioRatio定义的地方,符合我们的猜想

当然,你可以手工调整worker线程个数以及每个ioRatio来使得应用程序的性能最优

代入第一小节的公式,理论上两个自由参数必须满足
Nthread
= (1 + Tio/Tcpu) Ncpu
= {1 + ioTime/ [ioTime
(100 - ioRatio) / ioRatio]} Ncpu
= (100 / ioRatio)
Ncpu
其中 Ncpu 是常量,表示cpu的个数

netty中线程的命名

程序示例还是如上,我们创建了一个单线程的boss线程,一个2倍cpu线程数的worker线程,在程序跑了一段时间之后,我们发现线程堆栈有如下线程

Paste_Image.png
这里,我的cpu核数为4,所以worker线程为8
netty中的默认Nio线程都是由DefaultThreadFactorynewThread()方法创建出来的

netty 给nio的命名规则为 prefix加上一个自增的id,接下来看下prefix的定义

发现prefix的规则是poolName和poolId(自增)通过 ‘-‘ 连接起来的,那么接下来我们就要看看poolName在哪里初始化的,一个poolName对应一个EventLoopGroup,在EventLoopGroup的父类MultithreadEventLoopGroup中,我们找到了如下方法

这里的getClass 方法返回的是NioEventLoopGroup类,然后我们继续跟进

继续

这里是将poolType传进去并且使用了首字母为小写的简单类名,所以我们这里可以确定poolName就是nioEventLoopGroup,综合前面的结论,netty中nio线程默认名为nioEventLoopGroup-2-1格式

 

2016.11.29重要更新

文章发出之后,陆续收到一些评论,为了不误导读者,这里申明一下:上文所提出的线程个数和ioRatio的关系是错误的,见该issue,官方的解释为:ioRatio只是用来控制io相关的任务,和线程的设置没啥关系,这里的io相关的任务并不是io密集的操作,大多数都是cpu用来拷贝字节块的操作,和io无关,所以前面的ioRatio和线程个数相关的小结当笑话看看就行~

如果你觉得看的不过瘾,想系统学习Netty原理,那么你一定不要错过我的Netty源码分析系列视频:https://coding.imooc.com/class/230.html



分类:
标签:
请先登录,再评论

暂无回复,快来写下第一个回复吧~

为你推荐

一文搞懂Netty内存管理
以下文章来源于匠心零度 ,作者零度冰炫 在学习Netty的时候,ByteBuf随处可见,但是如何高效分配ByteBuf还是很复杂的,Netty的池化内存分配这块还是比较难的,很多人学习过,看过但是还是
Netty堆外内存泄露排查与总结
导读Netty 是一个异步事件驱动的网络通信层框架,用于快速开发高可用高性能的服务端网络框架与客户端程序,它极大地简化了 TCP 和 UDP 套接字服务器等网络编程。Netty 底层基于 JDK 的
Netty + JavaFx 实战:仿桌面版微信聊天
作者:小傅哥博客:[https://bugstack.cn](https://bugstack.cn) 沉淀、分享、成长,让自己和他人都能有所收获!😄 一、前言本项目是作者小傅哥使用```JavaF
Netty堆外内存泄漏排查盛宴
如果使用了 Netty 堆外内存,那么可以自行监控堆外内存的使用情况,不需要借助第三方工具,我们是使用的“反射”拿到的堆外内存的情况。逐渐缩小范围,直到 Bug 被找到。当我们确认某个线程的执行带来 Bug 时,可单步执行,可二分执行,定位到某行代码之后,跟到这段代码,然后继续单步执行或者二分的方
netty源码分析之nio线程个数以及线程命名规则
netty是一个以高性能著称的网络通信框架,许多开源项目都使用了netty作为底层网络通信框架,如avro,dubbo,nats本文将从源码的角度讲述netty在确定线程个数方面如何保证应用程序性能最优,源码基于netty-4.1.6.Final
netty源码分析之揭开reactor线程的面纱(三)
读完本篇文章,你将了解到netty的异步task机制,定时任务的处理逻辑,这些细节可以更好地帮助你写出netty应用
netty源码分析之揭开reactor线程的面纱(一)
netty最核心的就是reactor线程,对应项目中使用广泛的NioEventLoop,那么NioEventLoop里面到底在干些什么事?netty是如何保证事件循环的高效轮询和任务的及时执行?又是如何来优雅地fix掉jdk的nio bug?带着这些疑问,本篇文章将庖丁解牛,带你逐步了解netty
netty源码分析之揭开reactor线程的面纱(二)
我们已经了解到netty reactor线程的第一步是轮询出注册在selector上面的IO事件(select),那么接下来就要处理这些IO事件(process selected keys),本篇文章我们将一起来探讨netty处理IO事件的细节