netty之NioEventLoop源码分析

本文深入探讨Netty的线程模型,包括NioEventLoop的创建、启动及执行逻辑,解析其如何利用多线程提高性能,并规避JDK空轮询等常见问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

  1. 本篇分析之前,先对自己提几个问题
    1.1 默认情况下,Netty服务端起多少线程?何时启动?
    1.2 Netty是如何解决jdk空轮询bug的?
    1.3 Netty如何保证异步串行无锁化?
  2. 本篇按照以下步骤进行解析
    2.1 NioEventLoop创建
    2.2 NioEventLoop启动
    2.3 NioEventLoop执行逻辑

NioEventLoop创建

在这里插入图片描述

  1. 默认nThreads=0,所以默认线程数为CPU核数*2
    在这里插入图片描述

线程执行器ThreadPerTaskExecutor

  1. 创建线程执行器ThreadPerTaskExecutor(见下图红色标记部分1)
    1.1 ThreadPerTaskExecutor的execute方法会根据线程工厂创建线程并立马启动(见下方第二个截图)
    1.2 跟进threadFactory.newThread会创建FastThreadLocalThread线程
    1.3 线程命名规则: prefix = poolName + ‘-’ + poolId.incrementAndGet() + ‘-’ + nextId.incrementAndGet()
    –>nioEventLoopGroup + '- ’ + 2 + xx
    在这里插入图片描述
    在这里插入图片描述

创建并初始化children

  1. 伪代码展示
    1.1 executor为上面解析的线程执行器
    EventExecutor[] children = = new EventExecutor[nThreads];
    for (int i = 0; i < nThreads; i ++) {
    	children[i] = newChild(executor, args);
    }
    
  2. 由下图可知由newChild()方法创建NioEventLoop
    在这里插入图片描述
  3. NioEventLoop初始化
    3.1 保存线程执行器ThreadPerTaskExecutor
    3.2 创建一个MpscQueue
    3.3 创建一个selector
    在这里插入图片描述
    (1) taskQueue:外部线程执行netty任务时,如果判断不是在NioEventLoop线程里执行,则塞到任务队列,然后由
    NioEventLoop线程去执行

创建新连接选择器chooser

  1. 伪代码展示
    	EventExecutor[] children  //数组里元素为上面创建的NioEventLoop
    	EventExecutorChooserFactory.EventExecutorChooser chooser = chooserFactory.newChooser(children);
    

在这里插入图片描述
2. 上图展示了新连接循环获取NioEventLoop的过程
3. netty对上述实现做了优化
3.1 在计算机底层&操作(二进制运算)比取模操作(底层库实现)高效
在这里插入图片描述

NioEventLoop启动

在这里插入图片描述

  1. 下图是启动入口
    在这里插入图片描述
    在这里插入图片描述
    1.1 在上图执行execute()方法之前,此时EventLoop中的thread属性为空

  2. 下图NioEventLoop启动的详细逻辑
    2.1 将任务放入队列taskQueue
    2.2 如果当前线程不是eventLoop的线程,则启动线程:startThread
    (1) 将当前线程赋值给eventLoop的thread属性
    (2)最终触发线程执行器的execute()方法:ThreadPerTaskExecutor#execute
    在这里插入图片描述

NioEventLoop执行

在这里插入图片描述

  1. 下图是NioEventLoop执行的入口
    1.1 由红色标记部分进入NioEventLoop#run()方法
    在这里插入图片描述
  2. NioEventLoop#run()方法解析
    	for (;;) {
    		if (!hasTasks()) {//队列中没有任务
    	   			strategy = select(curDeadlineNanos); //轮询注册到select的IO事件
    	    }
    	    if (strategy > 0) {
    	    	final long ioStartTime = System.nanoTime();
    	    	try{
    	    		processSelectedKeys(); //处理IO相关的逻辑
    	    	}finally{
    	    			final long ioTime = System.nanoTime() - ioStartTime;
    	    			ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio); //处理外部线程扔到taskQueue里的任务
    	    	}  	
    	    }
    	}
    

轮询注册到select的IO事件select()

在这里插入图片描述

  1. deadlineNanos
    1.1 如果没有设置deadlineNanos,则selector会一直阻塞,除非轮询到一个事件
    1.2 如果设置deadlineNanos<=0,则立即返回
    1.3 如果设置deadlineNanos>0,则设置超时时间
    在这里插入图片描述
  2. 通过调用栈发现,轮询到key会添加到SelectedSelectionKeySet
    2.1 SelectedSelectionKeySet底层是由数组SelectionKey[]存放key (netty的优化部分)
    (1)有set变为数组,降低查询时间复杂度
  3. 怎样将自定义的SelectedSelectionKeySet融入到Selector中
    3.1 入口在NioEventLoop#openSelector(),通过反射替换(见下图)
    在这里插入图片描述

处理IO相关的逻辑 processSelectedKeys()

  1. 遍历轮询到的key:selectedKeys
    1.1 取出key的attachment为NioChannel
    1.2 根据不同事件进行相应的处理(见下图)
    在这里插入图片描述

处理异步任务队列 runAllTasks

  1. 任务的聚合
    1.1 任务分为:普通任务和定时任务
  2. 任务的执行

避免jdk空轮询的bug:unexpectedSelectorWakeup

在这里插入图片描述

  1. 通过上图发现,当轮询次数超过阈值(默认512)后,重新构建Selector
  2. 构建方式
    2.1 创建新的selector
    2.2 将旧的selector的key转移到新的selector上
    2.3 删掉旧的selector上的key
final Selector oldSelector = selector;
final SelectorTuple newSelectorTuple = openSelector();
for (SelectionKey key: oldSelector.keys()) {
	Object a = key.attachment();
	int interestOps = key.interestOps();
	key.cancel(); //删掉旧的
    SelectionKey newKey = key.channel().register(newSelectorTuple.unwrappedSelector, interestOps, a);
       if (a instanceof AbstractNioChannel) {
           // Update SelectionKey
           ((AbstractNioChannel) a).selectionKey = newKey; //构建新的
       }
}
selector = newSelectorTuple.selector;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值