Netty的线程模型

文章目录

  • 前言
  • NIO的三种reactor模式
  • Netty对3种Reactor模式的支持
  • Netty内部如何实现Reactor线程模型
  • 在事件分发过程中,Netty如何避免竞争条件

前言

Netty的线程模型采用了Reactor模式,这是一种高性能的IO设计模式,它基于NIO多路复用。Netty支持多种线程模型,包括单线程模型、多线程模型和混合线程模型。


NIO的三种reactor模式

NIO(Java Non-blocking IO,即Java非阻塞I/O)的三种Reactor模式主要包括:单Reactor单线程模式、单Reactor多线程模式和多Reactor多线程模式。

  1. 单Reactor单线程模式:这是最简单的模式,其中只有一个Reactor线程负责监听和处理所有的客户端连接和IO事件。当Reactor线程收到IO事件后,会自行完成事件的读取、业务逻辑处理和响应发送。这种模式虽然简单,但存在性能瓶颈,因为所有的事件都在一个线程中处理,无法充分利用多核CPU的性能。
    在这里插入图片描述

  2. 单Reactor多线程模式:为了提升性能,这种模式引入了多线程来处理业务逻辑。Reactor线程仍然负责监听和接收客户端的连接和IO事件,但当事件到来时,它会将事件分发给后续的Worker线程池进行处理。这样,Reactor线程可以专注于事件的监听和分发,而Worker线程池可以并行处理多个事件,从而提高了系统的吞吐量。
    在这里插入图片描述

  3. 多Reactor多线程模式:这种模式是对单Reactor多线程模式的进一步优化。它引入了多个Reactor线程,每个Reactor线程负责监听和处理一部分客户端的连接和IO事件。当某个Reactor线程收到事件后,它会将事件分发给对应的Worker线程池进行处理。这种模式可以更好地利用多核CPU的性能,进一步提高系统的吞吐量和处理能力。
    在这里插入图片描述

总的来说,NIO的三种Reactor模式各有优缺点,适用于不同的场景和需求。在选择时,需要根据系统的性能需求、硬件资源等因素进行综合考虑。

Netty对3种Reactor模式的支持

Netty中的3种Reactor模式与对应的Netty使用示例如下表所示

Reactor模式Netty使用示例
Reactor 单线程模式EventLoopGroup eventGroup = new NioEventLoopGroup(1);
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(eventGroup);
非主从 Reactor 多线程模式EventLoopGroup eventGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(eventGroup);
主从 Reactor 多线程模式EventLoopGroup bossGroup= new NioEventLoopGroup();
EventLoopGroup workGroup= new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workGroup);

Netty内部如何实现Reactor线程模型

Netty内部实现Reactor线程模型主要依赖于其事件驱动和异步非阻塞的特性。Netty通过EventLoopEventLoopGroup等核心组件来实现高效的线程管理和事件分发,从而支持Reactor模式。

以下是Netty内部实现Reactor线程模型的关键步骤和组件:

  1. EventLoopGroup的创建

    • EventLoopGroup是Netty中用于处理I/O操作的多线程事件循环。它实际上是一个线程池,每个线程都包含一个EventLoop。对于服务端,通常会有两个EventLoopGroup:一个用于接收客户端连接(bossGroup),另一个用于处理已接收连接上的I/O操作(workerGroup)。
  2. EventLoop的创建与注册

    • 每个EventLoop都绑定到一个Selector上,用于监听网络事件。当一个新的连接建立时,该连接会被注册到某个EventLoopSelector上。

    • EventLoop内部维护了一个任务队列,用于存放待处理的I/O任务和定时任务。它还负责处理这些任务,并在必要时将任务分发给其他线程执行。

protected void run() {
    //Select计数
    int selectCnt = 0;
    for (;;) {
        try {
            int strategy;
            try {
                //计算Select的策略 <1>
                strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());
                switch (strategy) {
                case SelectStrategy.CONTINUE:
                    continue;

                case SelectStrategy.BUSY_WAIT:
                    // fall-through to SELECT since the busy-wait is not supported with NIO

                case SelectStrategy.SELECT:
                    //当没有普通任务时,返回定时任务最近一次要执行的时间,如果有没有定时任务则返回-1
                    long curDeadlineNanos = nextScheduledTaskDeadlineNanos();
                    if (curDeadlineNanos == -1L) {
                        //如果没有定时任务,则将最近执行时间设置为Integer的最大值
                        curDeadlineNanos = NONE; // nothing on the calendar
                    }
                    //设置下一次的唤醒时间
                    nextWakeupNanos.set(curDeadlineNanos);
                    try {
                        if (!hasTasks()) {
                            //select看是否有新增的感兴趣的事件
                            strategy = select(curDeadlineNanos);
                        }
                    } finally {
                        // This update is just to help block unnecessary selector wakeups
                        // so use of lazySet is ok (no race condition)
                        //延迟设置线程的唤醒时间阻塞不必要的Select唤醒
                        nextWakeupNanos.lazySet(AWAKE);
                    }
                    // fall through
                default:
                }
            } catch (IOException e) {
                // If we receive an IOException here its because the Selector is messed up. Let's rebuild
                // the selector and retry. https://github.com/netty/netty/issues/8566
                //重建Selector
                rebuildSelector0();
                //重置计数
                selectCnt = 0;
                handleLoopException(e);
                continue;
            }

            selectCnt++;
            cancelledKeys = 0;
            needsToSelectAgain = false;
            final int ioRatio = this.ioRatio;
            boolean ranTasks;
            if (ioRatio == 100) {
                try {
                    if (strategy > 0) {
                        //如果有新增的感兴趣的事件,则处理
                        processSelectedKeys();
                    }
                } finally {
                    // Ensure we always run tasks.
                    //所有的时间都用来处理IO事件,包括普通任务和定时任务,不限制时间
                    ranTasks = runAllTasks();
                }
            } else if (strategy > 0) {
                //记录当前时间
                final long ioStartTime = System.nanoTime();
                try {
                    //处理Channel的就绪事件
                    processSelectedKeys();
                } finally {
                    // Ensure we always run tasks.
                    //计算用来处理IO事件的时间,包括普通任务和定时任务,限制时间
                    //以处理Channel的就绪事件所需时间为基准计算执行所有任务需要的时间
                    final long ioTime = System.nanoTime() - ioStartTime;
                    ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                }
            } else {
                ranTasks = runAllTasks(0); // This will run the minimum number of tasks
            }
            //如果有任务执行过了或者有任务待执行,则重置select计数
            if (ranTasks || strategy > 0) {
                if (selectCnt > MIN_PREMATURE_SELECTOR_RETURNS && logger.isDebugEnabled()) {
                    logger.debug("Selector.select() returned prematurely {} times in a row for Selector {}.",
                            selectCnt - 1, selector);
                }
                //有新增的事件,或者任务执行过,则将空轮询次数置0
                selectCnt = 0;
            } else if (unexpectedSelectorWakeup(selectCnt)) { // Unexpected wakeup (unusual case)
                //针对意外唤醒,重置计数
                selectCnt = 0;
            }
        } catch (CancelledKeyException e) {
            // Harmless exception - log anyway
            if (logger.isDebugEnabled()) {
                logger.debug(CancelledKeyException.class.getSimpleName() + " raised by a Selector {} - JDK bug?",
                        selector, e);
            }
        } catch (Error e) {
            throw e;
        } catch (Throwable t) {
            handleLoopException(t);
        } finally {
            // Always handle shutdown even if the loop processing threw an exception.
            try {
                if (isShuttingDown()) {
                    //如果EventLoop状态是正在关闭、已关闭、已终止,则执行关闭逻辑,关闭Channel和Selector的绑定,关闭Channel
                    closeAll();
                    //确认是否可以关闭了
                    if (confirmShutdown()) {
                        //退出NioEventLoop线程循环
                        return;
                    }
                }
            } catch (Error e) {
                throw e;
            } catch (Throwable t) {
                handleLoopException(t);
            }
        }
    }
}

  1. 事件监听与分发

    • Selector监听到网络事件(如连接建立、数据可读等)时,它会通知对应的EventLoop
    • EventLoop根据事件的类型,从已注册的Channel中查找对应的ChannelPipeline,并将事件分发给ChannelPipeline中的ChannelHandler进行处理。
  2. ChannelHandler的链式处理

    • ChannelHandler是Netty中处理网络事件的核心组件。它们以链式的方式组织在ChannelPipeline中,每个ChannelHandler都可以对事件进行拦截和处理。
    • 当事件到达某个ChannelHandler时,该ChannelHandler可以决定是继续将事件传递给下一个ChannelHandler,还是直接处理并结束事件的传递。
  3. 异步非阻塞操作

    • Netty的所有I/O操作都是异步非阻塞的。这意味着当发起一个I/O操作时,Netty不会等待操作完成就立即返回。相反,它会将操作的结果或异常通过回调的方式通知给调用者。
    • 这种异步非阻塞的特性使得Netty能够高效地处理大量并发连接和事件,提高了系统的吞吐量和响应速度。
  4. 线程模型优化

    • Netty还支持多种线程模型优化技术,如多Reactor模式、零拷贝等,以进一步提高性能。
    • 在多Reactor模式中,Netty可以创建多个EventLoopGroup来处理不同类型的任务或不同优先级的任务,从而更好地利用系统资源。

通过上述组件和机制,Netty内部实现了高效的Reactor线程模型,使得开发者能够轻松地构建高性能、高可靠性的网络应用。

在事件分发过程中,Netty如何避免竞争条件

Netty在事件分发过程中通过一系列机制来避免竞争条件,确保线程安全和高效的事件处理。以下是一些关键的避免竞争条件的策略:

  1. 单线程模型中的事件处理
    在单线程Reactor模型中,虽然存在竞争条件的潜在风险,但由于所有事件都在一个线程中处理,因此实际上避免了多线程间的竞争。然而,这种模型在处理高并发时可能会成为瓶颈,因为任何事件的处理都会阻塞其他事件的处理。

  2. 多线程模型中的线程隔离
    对于多线程模型(如主从多线程Reactor模型),Netty通过将不同类型的任务分配给不同的线程或线程组来减少竞争。例如,接受新连接的任务和处理已建立连接上I/O任务可以被分配给不同的线程池。这样,处理不同任务的线程之间就不会发生竞争。

  3. 无锁化设计
    Netty在内部实现中尽可能使用无锁数据结构和算法,以减少锁竞争的开销。无锁数据结构通过原子操作和内存可见性保证线程安全,避免了传统锁机制带来的性能开销和潜在的死锁问题。

  4. 细粒度的锁策略
    当需要使用锁时,Netty会采用细粒度的锁策略,只对需要同步的共享资源加锁,以减少锁的粒度,从而降低锁竞争的可能性。这通常涉及到对关键部分的精确分析和优化。

  5. 事件队列的线程安全
    事件队列是Netty中事件分发的重要组件,它必须是线程安全的。Netty通过内部同步机制确保多个线程对事件队列的并发访问不会导致数据不一致或其他竞争条件。

  6. 事件分发的顺序性
    Netty确保事件按照它们发生的顺序进行分发,这有助于避免由于事件乱序导致的竞争条件。例如,对于同一个Channel的事件,Netty会保证它们按照发生的顺序被处理。

  7. 避免共享状态
    尽量减少跨线程共享的状态可以大大降低竞争条件的风险。Netty鼓励开发者使用局部变量或线程局部变量来存储状态,而不是依赖全局变量或共享对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/517917.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

226.翻转二叉树

226.翻转二叉树 力扣题目链接(opens new window) 翻转一棵二叉树。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNo…

Linux操作系统之防火墙、redis安装

目录 一、防火墙 1、防火墙的类别 2、安装iptables(四表五链&#xff09; 一、防火墙 1、防火墙的类别 安全产品 杀毒 针对病毒&#xff0c;特征篡改系统中文件杀毒软件针对处理病毒程序 防火墙 针对木马&#xff0c;特征系统窃密 防火墙针对处理木马 防火墙分为两种 硬件…

MySql 实战大数据查询-(表分区实现)

一 mysql分区&#xff1a; 分区是将单个表按照某种规则划分成多个子集&#xff0c;每个子集称为一个分区。常见的分区策略包括按照时间范围、范围值、列表等进行分区。 优点&#xff1a; 查询性能更好&#xff0c;涉及分区键的查询&#xff0c;数据库引擎可以只扫描特定分区&…

【MySQL】聚合函数和分组聚合

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习计网、mysql和算法 ✈️专栏&#xff1a;MySQL学习 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac…

Qt+OpenGL_part1

OpenGL&#xff0c;Qt实现&#xff1a;1入门篇(已更完)_哔哩哔哩_bilibili OpenGL3.3以上是现代模式&#xff08;可编程管线&#xff09;&#xff1a; 状态机 状态设置函数&#xff08;State-changing Function) 状态应用函数 &#xff08;State-using Function) OpenGL的状态…

人脸表情识别——数据集分享(内含处理过的AffectNet数据集)

前言&#xff1a; 最近终于是把第一篇论文发出去了&#xff0c;半年前我还挣扎在复现不出来论文的精度之中&#xff0c;这里指的是AffectNet和FERPlus这俩个数据集精度复现不出来&#xff0c;Raf-db数据集是可以复现出精度的。 接下来说重点&#xff1a;之前之所以另外两个数据…

【动手学深度学习】深入浅出深度学习之RMSProp算法的设计与实现

目录 &#x1f31e;一、实验目的 &#x1f31e;二、实验准备 &#x1f31e;三、实验内容 &#x1f33c;1. 认识RMSProp算法 &#x1f33c;2. 在optimizer_compare_naive.py中加入RMSProp &#x1f33c;3. 在optimizer_compare_mnist.py中加入RMSProp &#x1f33c;4. 问…

MacOS - brew 和 brew cask 有什么区别?

brew 是 ruby 的包管理&#xff0c;后来看 yangzhiping 的博客介绍了 brew cask&#xff0c;感觉 cask 是更好的关联关系管理&#xff0c;但是&#xff0c;我后来使用过程中&#xff0c;发现很多软件 brew cask 里没有&#xff0c;但是 brew 里面倒是挺多&#xff01;今天来给说…

电梯轿厢内电动车数据集,VOC标签格式已标注(数据集+训练好的权重)

本数据集用于电梯禁入电动车项目的目标检测算法模型训练任务。 共有4000张左右图片&#xff0c;全部为电梯监控真实照片&#xff0c;没有网络爬虫滥竽充数的图片&#xff0c;并已经分好数据集和验证集&#xff0c;可直接用来训练。以上图片均一一手工标注&#xff0c;标签格式为…

深入浅出 -- 系统架构之分布式架构

​​​​​​分布式架构&#xff1a; 根据业务功能对系统做拆分&#xff0c;每个业务功能模块作为独立项目开发&#xff0c;称为一个服务。 当垂直应用越来越多时&#xff0c;应用之间的交互不可避免&#xff0c;可将共用的基础服务或核心模块抽取出来作为独立服务&#xff0c…

golang 和java对比的优劣势

Golang&#xff08;或称Go&#xff09;和Java都是非常流行的编程语言&#xff0c;被广泛应用于各种领域的软件开发。尽管它们都是高级编程语言&#xff0c;但它们具有许多不同的特性和适用场景。本文将重点比较Golang和Java&#xff0c;探讨它们的优势和劣势。 性能方面&#…

java数据结构与算法刷题-----LeetCode504. 七进制数

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 1. 倒退迭代&#xff08;除基取余法&#xff09;2. 省略掉反转操…

基于java+SpringBoot+Vue的房屋租赁系统设计与实现

基于javaSpringBootVue的房屋租赁系统设计与实现 开发语言: Java 数据库: MySQL技术: Spring Boot JSP工具: IDEA/Eclipse、Navicat、Maven 系统展示 前台展示 房源浏览模块&#xff1a;展示可租赁的房源信息&#xff0c;用户可以根据条件筛选房源。 预约看房模块&#…

武汉星起航电子商务有限公司挂牌展示,为合作伙伴提供全方位支持

随着跨境电商领域市场竞争愈演愈烈&#xff0c;武汉星起航亚马逊一站式孵化平台悄然崭露头角。从2017年起&#xff0c;武汉星起航便立足亚马逊自营店铺&#xff0c;积累了丰富的实战运营经验。2020年正式成立后&#xff0c;公司以跨境电商为核心&#xff0c;凭借专业的运营团队…

STM32CubeIDE基础学习-定时器PWM实验

STM32CubeIDE基础学习-定时器PWM实验 文章目录 STM32CubeIDE基础学习-定时器PWM实验前言第1章 硬件介绍第2章 工程配置2.1 基础工程配置部分2.2 生成工程代码部分 第3章 代码编写3.1 查看PWM波3.2 设置单个比较值3.3 呼吸灯 第4章 实验现象总结 前言 在平时单片机开发时&#…

Midjourney艺术家分享|By Moebius

Moebius&#xff0c;本名让吉拉德&#xff08;Jean Giraud&#xff09;&#xff0c;是一位极具影响力的法国漫画家和插画师&#xff0c;以其独特的科幻和幻想风格而闻名于世。他的艺术作品不仅在漫画领域内受到高度评价&#xff0c;也为电影、时尚和广告等多个领域提供了灵感。…

MATLAB多级分组绘图及图例等细节处理 ; MATLAB画图横轴时间纵轴数值按照不同sensorCode分组画不同sensorCode的曲线

平时研究需要大量的绘图Excel有时候又臃肿且麻烦 尤其是当处理大量数据时可能会拖死Windows 示例代码及数据量展示 因为数据量是万级别的折线图也变成"柱状图"了, 不过还能看出大致趋势! 横轴是时间纵轴是传感器数值图例是传感器所在深度 % data readtable(C:\U…

Vue依赖注入,详细解析

Prop 逐级透传问题​ 通常情况下&#xff0c;当我们需要从父组件向子组件传递数据时&#xff0c;会使用 props。想象一下这样的结构&#xff1a;有一些多层级嵌套的组件&#xff0c;形成了一颗巨大的组件树&#xff0c;而某个深层的子组件需要一个较远的祖先组件中的部分数据。…

设计模式 --5观察者模式

观察者模式 观察者模式的优缺点 优点 当一个对象改变的时候 需要同时改变其他对象的相关动作的时候 &#xff0c;而且它不知道有多少具体的对象需要改变 应该考虑使用观察者模式 。观察者模式的工作就是解除耦合 让耦合双方都依赖与抽象 而不是具体 是的各自改变都不会影响另…

RedHat9中KVM虚拟机的配置与管理

KVM虚拟技术介绍 Linux的KVM&#xff08;Kernel-based Virtual Machine&#xff09;虚拟技术是一种基于Linux内核的虚拟化解决方案。它允许在单个物理服务器上创建和运行多个隔离的虚拟机&#xff0c;每个虚拟机都有自己的操作系统和应用程序&#xff0c;就像运行在独立的物理…