专栏名称: zhisheng
Java攻城狮
目录
相关文章推荐
芋道源码  ·  MySQL数据实时同步到Elasticsea ... ·  昨天  
芋道源码  ·  300 秒到 4 秒,如何将 MySQL ... ·  昨天  
Java编程精选  ·  字节员工自曝:再强调一遍OD ... ·  昨天  
芋道源码  ·  船新 IDEA 2025.1 要来了,新特性真香! ·  2 天前  
芋道源码  ·  几个必会的JDK性能监控和故障处理工具 ·  3 天前  
51好读  ›  专栏  ›  zhisheng

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

zhisheng  · 掘金  · Java  · 2018-07-15 01:20

正文

概述

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的定义