Netty实战(七)

news2025/1/10 20:33:13

EventLoop和线程模型

  • 一、什么是线程模型
  • 二、EventLoop 接口
    • 2.14 Netty 4 中的 I/O 和事件处理
  • 三、任务调度
    • 3.1 JDK 的任务调度 API
    • 3.2 使用 EventLoop 调度任务
  • 四、实现细节
    • 4.1 线程管理
    • 4.2 EventLoop/线程的分配
      • 4.2.1 异步传输
      • 4.2.2 .阻塞传输

一、什么是线程模型

简单地说,线程模型指定了操作系统、编程语言、框架或者应用程序的上下文中的线程管理的关键方面。

在早期的 Java 语言中,我们使用多线程处理的主要方式无非是按需创建和启动新的 Thread 来执行并发的任务单元,这种在高负载下表现得很原始。Java 5 随后引入了 Executor API,其线程池通过缓存和重用Thread 极大地提高了性能。

基本的线程池化模式可以描述为:

  • 从池的空闲线程列表中选择一个 Thread,并且指派它去运行一个已提交的任务(一个Runnable 的实现);
  • 当任务完成时,将该 Thread 返回给该列表,使其可被重用。

就像这样,线程再步骤3和4之间无限循环:
在这里插入图片描述

这种模式有个缺陷就是:
虽然池化和重用线程相对于简单地为每个任务都创建和销毁线程是一种进步,但是它并不能消除由上下文切换所带来的开销。这种开销将随着线程数量的增加很快变得明显,并且在高负载下愈演愈烈。此外,仅仅由于应用程序的整体复杂性或者并发需求,在项目的生命周期内也可能会出现其他和线程相关的问题。

二、EventLoop 接口

首先介绍一个术语: 运行任务来处理在连接的生命周期内发生的事件是任何网络框架的基本功能。与之相应的编程上的构造通常被称为事件循环

下面我们展示了一个事件循环的基本思想,其中每个任务都是一个 Runnable 的实例。

while (!terminated) {
//阻塞,直到有事件已经就绪可被运行
List<Runnable> readyEvents = blockUntilEventsReady();
//遍历处理事件
for (Runnable ev: readyEvents) {
ev.run();
}
}

在Netty中 使用了 interface io.netty.channel.EventLoop来搭配事件循环。它采用了两个基本的 API:并发和网络编程。

  • 首先,io.netty.util.concurrent 包构建在 JDK 的 java.util.concurrent 包上,用来提供线程执行器。
  • 其次,io.netty.channel 包中的类,为了与 Channel 的事件进行交互,扩展了这些接口/类。

它的的类层次结构就像下面一样:

在这里插入图片描述
看起来比较复杂,我们慢慢看。

  • 首先,在这个模型中,一个 EventLoop 将由一个永远都不会改变的 Thread 驱动,同时任务(Runnable 或者 Callable)可以直接提交给 EventLoop 实现,以立即执行或者调度执行。

  • 其次,根据配置和可用核心的不同,可能会创建多个 EventLoop 实例用以优化资源的使用,并且单个
    EventLoop 可能会被指派用于服务多个 Channel。

需要注意的是,Netty的EventLoop在继承了ScheduledExecutorService的同时,只定义了一个方法,parent(),如下面的代码片断所示,用于返回到当前EventLoop实现的实例所属的EventLoopGroup的引用。

public interface EventLoop extends EventExecutor, EventLoopGroup {
@Override
EventLoopGroup parent();
}

事件/任务的执行顺序 事件和任务是以先进先出(FIFO)的顺序执行的。这样可以通过保证字节内容总是按正确的顺序被处理,消除潜在的数据损坏的可能性。

2.14 Netty 4 中的 I/O 和事件处理

上一篇我们说到过 I/O 操作触发的事件将流经安装了一个或者多个ChannelHandler 的 ChannelPipeline。传播这些事件的方法调用可以随后被 ChannelHandler 所拦截,并且可以按需地处理事件。

事件的性质通常决定了它将被如何处理;它可能将数据从网络栈中传递到你的应用程序中,或者进行逆向操作,或者 执行一些截然不同的操作。

但是事件的处理逻辑必须足够的通用和灵活,以处理所有可能的用例。因此,在Netty 4 中,所有的I/O操作和事件都由已经被分配给了EventLoop的那个Thread来处理。

三、任务调度

为什么要进行任务调度?
偶尔,你将需要调度一个任务以便稍后(延迟)执行或者周期性地执行。例如,你可能想要注册一个在客户端已经连接了 5 分钟之后触发的任务。一个常见的用例是,发送心跳消息到远程节点,以检查连接是否仍然还活着。如果没有响应,你便知道可以关闭该 Channel了。

3.1 JDK 的任务调度 API

Java 5 之前,任务调度是建立在 java.util.Timer 类之上的,其使用了一个后台Thread,并且具有与标准线程相同的限制。随后JDK 提供了 java.util.concurrent 包,它定义interface ScheduledExecutorService。

下面展示一个ScheduledExecutorService 调度任务:

//创建一个其线程池具有 10 个线程的ScheduledExecutorService
ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);
ScheduledFuture<?> future = executor.schedule(
//创建一个 Runnable,以供调度稍后执行
new Runnable() {
@Override
public void run() {
//该任务要打印的消息
System.out.println("60 seconds later");
}
}, 
//调度任务在从现在开始的 60 秒之后执行
60, TimeUnit.SECONDS);
...
//一旦调度任务执行完成,就关闭ScheduledExecutorService 以释放资源
executor.shutdown();

虽然 ScheduledExecutorService API 是直截了当的,但是在高负载下它将带来性能上的负担。

ScheduledExecutorService 的实现具有局限性,例如,事实上作为线程池管理的一部分,将会有额外的线程创建。如果有大量任务被紧凑地调度,那么这将成为一个瓶颈。

3.2 使用 EventLoop 调度任务

Netty 通过 Channel 的 EventLoop 实现任务调度解决了ScheduledExecutorService 的局限性

Channel ch = ...
ScheduledFuture<?> future = ch.eventLoop().schedule(
//创建一个 Runnable,以供调度稍后执行
new Runnable() {
//要执行的代码
@Override
public void run() {
System.out.println("60 seconds later");
}
//调度任务在从现在开始的 60 秒之后执行
}, 60, TimeUnit.SECONDS);

经过 60 秒之后,Runnable 实例将由分配给 Channel 的 EventLoop 执行。如果要调度任务以每隔 60 秒执行一次,可以使用 scheduleAtFixedRate()方法。

Channel ch = ...
ScheduledFuture<?> future = ch.eventLoop().scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
System.out.println("Run every 60 seconds");
}
}, 60, 60, TimeUnit.Seconds);

Netty的EventLoop扩展了ScheduledExecutorService,所以它提供了使用JDK实现可用的所有方法,包括在前面的示例中使用到的schedule()和scheduleAtFixedRate()方法。

要想取消或者检查(被调度任务的)执行状态,可以使用每个异步操作所返回的 ScheduledFuture。

//调度任务,并获得所返回的ScheduledFuture
ScheduledFuture<?> future = ch.eventLoop().scheduleAtFixedRate(...);
// 其他一些运行的代码......
boolean mayInterruptIfRunning = false;
//取消该任务,防止它再次运行
future.cancel(mayInterruptIfRunning);

四、实现细节

4.1 线程管理

Netty线程模型的卓越性能取决于对于当前执行的Thread的身份的确定。

如果(当前)调用线程正是支撑 EventLoop 的线程,那么所提交的代码块将会被(直接)执行。否则,EventLoop 将调度该任务以便稍后执行,并将它放入到内部队列中。当 EventLoop下次处理它的事件时,它会执行队列中的那些任务/事件。(这也就解释了任何的 Thread 是如何与 Channel 直接交互而无需在 ChannelHandler 中进行额外同步的)

注意,每个 EventLoop 都有它自已的任务队列,独立于任何其他的 EventLoop。

EventLoop 的执行逻辑

在这里插入图片描述

注意:不要将一个长时间运行的任务放入到执行队列中,因为它将阻塞需要在同一线程上执行的任何其他任务。如果必须要进行阻塞调用或者执行长时间运行的任务,建议使用一个专门的EventExecutor。(上一篇强调过)

4.2 EventLoop/线程的分配

服务于 Channel 的 I/O 和事件的 EventLoop 包含在 EventLoopGroup 中。根据不同的传输实现,EventLoop 的创建和分配方式也不同。

4.2.1 异步传输

异步传输实现只使用了少量的 EventLoop(以及和它们相关联的 Thread),而且在当前的线程模型中,它们可能会被多个 Channel所共享。这使得可以通过尽可能少量的 Thread 来支撑大量的 Channel,而不是每个 Channel 分配一个 Thread。

下面展示了一个 EventLoopGroup,它具有 3 个固定大小的 EventLoop(每个 EventLoop都由一个 Thread 支撑)。在创建 EventLoopGroup 时就直接分配了 EventLoop(以及支撑它们的 Thread),以确保在需要时它们是可用的。

在这里插入图片描述
我们简单的过滤一下上图的内容:

1、EventLoopGroup 负责为每个新创建的 Channel 分配一个 EventLoop。在当前实现中,使用顺序循环(round-robin)的方式进行分配以获取一个均衡的分布,并且相同的 EventLoop可能会被分配给多个 Channel。

2、一旦一个 Channel 被分配给一个 EventLoop,它将在它的整个生命周期中都使用这个EventLoop(以及相关联的 Thread)。

3、一个EventLoop 通常会被用于支撑多个 Channel,所以对于所有相关联的 Channel 来说,ThreadLocal 都将是一样的。

ThreadLocal共享使得它对于实现状态追踪等功能来说是个糟糕的选择。然而,在一些无状态的上下文中,它仍然可以被用于在多个 Channel之间共享一些重度的或者代价昂贵的对象,甚至是事件。

4.2.2 .阻塞传输

OIO传输中每一个 Channel 都将被分配给一个 EventLoop(以及它的 Thread)。如果你开发的应用程序使用过 java.io 包中的阻塞 I/O 实现,你可能就遇到过这种模型。

在这里插入图片描述
这种模型和之前一样,得到的保证是每个 Channel 的 I/O 事件都将只会被一个 Thread(用于支撑该 Channel 的 EventLoop 的那个 Thread)处理。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java基础学习---3、堆、GC

1、堆 1.1 概述 1.1.1 堆空间结构 1.1.2 堆空间工作机制 新创建的对象会放在Eden区当Eden区中已使用的空间达到一定比例&#xff0c;会触发Minor GC每一次在Minor GC中没有被清理掉的对象就成了幸存者。幸存者对象会被转移到幸存者区幸存者区分成from区和to区from区快满的时…

如何提高软件复用度,降低项目开发成本?

1、代码基线管控策略 理想的代码复用是我们建立一条主干代码&#xff0c;持续维护下去。面对客户的新需求&#xff0c;需要我们拉一条临时分支来满足客户需求&#xff0c;然后将稳定后的临时分支代码成果回归到主干。这样我们所有的研发成果都可以在一个代码分支上进行追溯&…

FreeRTOS学习之路,以STM32F103C8T6为实验MCU(序章——浅谈单片机以及FreeRTOS)

学习之路主要为FreeRTOS操作系统在STM32F103&#xff08;STM32F103C8T6&#xff09;上的运用&#xff0c;采用的是标准库编程的方式&#xff0c;使用的IDE为KEIL5。 注意&#xff01;&#xff01;&#xff01;本学习之路可以通过购买STM32最小系统板以及部分配件的方式进行学习…

论文解读 | 透过窥镜: 透明容器内物体的神经三维重建

原创 | 文 BFT机器人 随着虚拟现实和虚拟世界技术的发展&#xff0c;博物馆藏品的数字化是一个越来越受关注的新兴话题。世界上许多著名的博物馆都在为网上展览建立自己的数字馆藏。 在这些藏品中&#xff0c;有一种特殊而重要的藏品昆虫、人体组织、水生生物和其他易碎的标本需…

ZooKeeper(一):基础介绍

文章目录 什么是 ZooKeeper&#xff1f;ZooKeeper 发展历史ZooKeeper 应用场景ZooKeeper 服务的使用ZooKeeper 数据模型data tree 接口znode 分类 总结 什么是 ZooKeeper&#xff1f; ZooKeeper 是一个分布式的&#xff0c;开放源码的分布式应用程序协同服务。ZooKeeper 的设计…

docker-compose安装nacos 2.2.1及配置

目录 官网 创建存储目录 创建数据库 application.properties配置&#xff08;重要&#xff09; docker-compose.yml 启动 登录 下面是安装nacos 2.2.1版本的方法&#xff0c;有一些变化 官网 GitHub - alibaba/nacos: an easy-to-use dynamic service discovery, configu…

SCTracker 跟踪论文阅读笔记

SCTracker 跟踪论文阅读笔记 SCTracker: Multi-object tracking with shape and confidence constraints 论文链接 (未开源状态) 论文主要更新点围绕shape constraint and confidence两点来展开&#xff1a; 首先论证在跟踪匹配的过程中D-box(检测框)与T-box(预测框)需要有一定…

今日的CSS小案例

个人名片&#xff1a; &#x1f60a;作者简介&#xff1a;一名大一在校生&#xff0c;web前端开发专业 &#x1f921; 个人主页&#xff1a;几何小超 &#x1f43c;座右铭&#xff1a;懒惰受到的惩罚不仅仅是自己的失败&#xff0c;还有别人的成功。 &#x1f385;**学习目…

第一篇、基于Arduino uno,获取dht11温湿度传感器的温度信息和湿度信息——结果导向

0、结果 说明&#xff1a;先来看看串口调试助手显示的结果&#xff0c;如果是你想要的&#xff0c;可以接着往下看。 1、外观 说明&#xff1a;虽然dht11温湿度模块形态各异&#xff0c;但是代码都是适用的&#xff0c;因为它们的模块都是一样的。 2、连线 说明&#xff1a;…

微博开发--微博官方API使用方法【从注册到实战】

第一步&#xff1a;微博开发者身份认证 访问微博开放平台&#xff0c;登录自己微博账号&#xff0c;登录之后首先需要完善开发者的基本信息。【使用个人】 填写完成之后【审核通过】如下&#xff1a; 第二步&#xff1a;创建自己的应用 【备注&#xff1a;如果只是为了测试…

逻辑回归及逻辑回归的评估指标

一、逻辑回归介绍 逻辑回归(Logistic Regression)是机器学习中的一种分类模型&#xff0c;逻辑回归是一种分类算法&#xff0c;虽然名字中带有回归&#xff0c;但是它与回归之间有一定的联系。由于算法的简单和高效&#xff0c;在实际中应用非常广泛。 1.逻辑回归的应用场景 …

【腾讯云FinOps Crane 集训营】快速搭建一个 Kubernetes+Crane 环境,以及如何基于 Crane 优化你的集群和应用初体验

文章目录 一、活动介绍二、环境搭建三、安装本地的 Kind 集群和 Crane 组件四、界面截图五、主要功能六、整体架构七、Crane的优势八、总结参考文献 一、活动介绍 Crane 是由腾讯云主导开源的国内第一个基于云原生技术的成本优化项目&#xff0c;遵循 FinOps 标准&#xff0c;…

用java带你了解网络IO模型

目录 1.BIO1.1 简述1.2 代码示例1.3优点和缺点1.4 思考 2. NoBlockingIO2.1 简述2.2 代码示例2.3 优点和缺点2.4 思考 3. NIO&#xff08;NewIO&#xff09;3.1 简述3.2 代码示例3.3 优点和缺点3.3 思考 4. 扩展select/poll、epoll4.1 简述4.2 select/poll4.3 epoll4.4 扩展话题…

Linux之vim编辑器的使用

目录 一、vim是什么&#xff1f; 试验1&#xff1a; 二.命令模式继承用法&#xff1a; vim命令模式的快捷键: 光标移动: vim文本复制相关操作: vim文本编辑操作: 三.末行模式命令用法 部分快捷键&#xff1a; 四.vim编辑器的配置原理 一、vim是什么&#xff1f; vi…

Hive SQL语句的正确执行顺序

关于 sql 语句的执行顺序网上有很多资料&#xff0c;但是大多都没进行验证&#xff0c;并且很多都有点小错误&#xff0c;尤其是对于 select 和 group by 执行的先后顺序&#xff0c;有说 select 先执行&#xff0c;有说 group by 先执行&#xff0c;到底它俩谁先执行呢&#x…

智能结构诊断器:建筑结构健康的守护者

近年来&#xff0c;接二连三的自建房坍塌&#xff0c;超高层建筑震动&#xff0c;让建筑的健康和安全性成为了人们关注的焦点。为了确保建筑物的长期稳定性和安全性&#xff0c;迫切需要高效且准确的方法来监测结构的健康状况。智能结构诊断器的出现&#xff0c;让建筑结构监测…

大数据如何助力营销(5)活动复盘

在市场竞争日益激烈的今天&#xff0c;营销活动已经成为吸引用户、提升品牌影响力、增加销售转化的重要手段。然而&#xff0c;一场营销活动在举办后&#xff0c;往往难以评估活动的效果&#xff0c;而大数据技术将从方方面面、科学有效地复盘活动&#xff0c;并为下一次举办活…

chatgpt赋能Python-python_kbhit

Python kbhit - 帮助您掌握实时按键输入 如果您需要使用实时按键输入来控制您的Python程序&#xff0c;那么你需要知道的是Python kbhit。kbhit是一种允许用户实时按键输入并立即响应的技术。本文将介绍Python kbhit的用途和用法&#xff0c;并探讨实时输入如何帮助您掌控程序…

AI绘图学习心得分享-Midjourney绘画AI,让你少走一些弯路

本教程收集于:AIGC从入门到精通教程 AI绘图学习心得分享-Midjourney绘画AI,让你少走一些弯路 本篇没有什么长篇大论,全部都是实用心得总结。接下来,我们将分享关于Midjourney绘画AI的实用心得总结,包括构图指令结构、常用指令、操作技巧、常用风格词汇和构图词汇。 如果…

手机号码在网时长 API 实现广告投放和精准营销案例分析

引言 手机在网时长是指用户在移动网络上的在线时间&#xff0c;包括用户接入网络的时间和断开网络的时间。手机在网时长 API 是一种提供手机在网时长数据的编程接口&#xff0c;为开发者和服务提供商提供了获取和利用这些数据的能力。 本文旨在深入探讨手机在网时长 API 的技…