当前位置: 首页 > news >正文

如何用一个例子向10岁小孩解释高并发实时服务的单线程事件循环架构

I/O密集型进程和CPU密集型进程

聊天应用程序、MMO(大型多人在线)游戏、金融交易系统、等实时服务需要处理大量并发流量和实时数据。

这些服务是I/O密集型的,因为它们花费大量资源处理输入输出操作,例如高吞吐量、低延迟网络通信(客户端与服务器以及其他应用程序组件之间)、实时数据库写入、文件 I/O、与第三方 API 的通信、流式传输实时数据等等。

通常,IO 密集型进程的性能取决于服务器的I/O系统,I/O中(如写数据到磁盘)的任何延迟都可能导致系统瓶颈。在I/O密集型进程中,CPU使用率相对较少,它需要等待I/O过程完成才能执行某个进程。

而在CPU密集型进程中,性能主要取决于CPU的速度。系统大部分时间都花在执行 CPU 中的进程上,而不是与外部组件通信。CPU性能越好,系统性能就越好。

如上所述,实时并发应用程序中的关键进程(例如高吞吐量网络操作、数据库写入、组件间通信等)会由于 IO 操作而引入系统延迟。

为了保证低延迟,不同 的Web 框架利用不同的策略(例如非阻塞 IO、单线程架构的异步事件处理、参与者模型、反应式编程等)来实现可扩展的实时服务。

在本文中,将讨论 NodeJS的单线程事件循环模型架构来处理大量 IO 密集型进程。

让我们开始吧。

在深入研究单线程架构之前,让我们先了解一下传统的基于线程请求的模型存在的问题。

基于线程的同步模型的 IO 瓶颈

在传统的基于线程的同步模型中,应用服务器利用该模型来处理客户端请求时,对于 I/O 密集型应用服务,会面临请求吞吐量瓶颈。

以Apache Tomcat服务器为例,它会维护一个线程池,当它接收到客户端请求时,该请求会被分配给线程池中的一个工作线程来处理,详细流程如下:

  • 客户端发送一个请求到Web服务器;
  • Web服务器收到请求后,从线程池中选择一个空闲可用的线程用于处理该请求;
  • 此线程读取客户端请求,处理客户端请求,执行阻塞的IO操作(如果需要)和准备响应;
  • 此线程将准备好的请求发送回Web服务器;
  • Web服务器又将此响应发送到相应的服务器。

服务器为所有客户端执行以上步骤,为每一个客户端请求尽量分配一个线程,如果线程池可用线程数少于并发请求数时,则在使用完所有线程之后,剩余的客户端请求会在队列中等待。

而在I/O密集型应用中,大多数请求都会执行 IO 操作,例如,向数据库发出查询。在这种情况下,只要来自服务器的请求正在等待来自数据库的响应,该工作线程就会暂时被阻塞。它无法处理对服务器的其他请求。

因此如果这些线程中有大量的阻塞IO操作(例如:和数据库、文件系统、外部服务等交互),那么剩余的客户端将会等待很长的时间。

可用看出在高并发流量的 I/O 密集型应用中,这种线程阻塞行为会导致资源争用、并发性降低和性能瓶颈。

解决 IO 瓶颈问题

不同的编程语言和各自生态系统会采用一些异步方法(单线程事件循环模型、Actor模型、响应式)来解决同步请求阻塞问题。本文主要介绍NodeJS的架构和单线程事件循环模型。

NodeJS 从最基本的设计出发,目的就在于通过其单线程事件循环架构,以最小的开销高效处理大量并发请求和异步 IO 操作。作为主线程处理所有客户端请求,并将所有 IO 操作委托给其它线程,详细流程如下。

  • 客户端发送请求到Web服务器;
  • NodeJS的Web服务器在内部维护一个有限的线程池,以便为客户端请求提供服务;
  • NodeJS的Web服务器接收这些请求并将它们放入队列(Event Queue)中。 它被称为“事件队列”
  • NodeJS的Web服务器内部有一个组件,称为“事件循环(Event Loop Single Thread)”,从英文名可以看出,事件循环只使用到了一个线程,使用无限循环来接收请求并处理它们。它是NodeJS的处理模型的核心
  • 事件循环回去检查是否有客户端的请求被放置在事件队列中。如果没有,会一直等待事件队列中存在请求。
  • 如果事件队列中有需要处理的客户端请求,则会从事件队列中选择一个请求。

在事件循环线程处理客户端请求时,根据请求的类型,有不同的处理方式:

  • 如果该客户端请求不需要任何阻塞IO操作,则处理所有内容,准备响应并将其发送回客户端
  • 如果该客户端请求需要一些阻塞IO操作,例如与数据库,文件系统,外部服务交互,就会从从内部线程池获取一个可用的线程并将此客户端请求分配给该线程,这个内部线程池的线程负责接收该请求,处理该请求,执行阻塞IO操作,准备响应并将其发送回事件循环,事件循环依次将响应发送到相应的客户端

以上图为例,Web服务器内部维护着一个有限的线程池,线程池中线程数量为m个,NodeJS的Web服务器接收到Client-1, Client-2, …, Client-n的请求后,将请求放入到事件队列中NodeJS的事件循环从队列中开始拾取这些请求,以Client-1的请求和Client-3为例。

对于Client-1的请求:

  • 事件循环检查Client-1 Request-1是否确实需要任何阻塞IO操作,或者需要更多时间来执行复杂的计算任务
  • 由于此请求是简单计算和非阻塞IO任务,因此不需要单独的线程来处理它
  • 事件循环处理该请求所需要的操作,准备其响应Response-1
  • 事件循环发送Response-1到Client-1

对于Client-3的请求:

  • 检查Client-n Request-n是否需要任何阻塞IO操作或花费更多时间来执行复杂的计算任务
  • 由于此请求有非常复杂的计算或阻塞IO任务,因此事件循环不会处理此请求
  • 事件循环从内部线程池中获取线程T-1,并将此Client-n Request-n分配给线程T-1
  • 线程T-1读取并处理Request-n,执行必要的阻塞IO或计算任务,最后准备响应Response-n
  • 线程T-1将此Response-n发送到事件循环,事件循环依次将此Response-n发送到Client-n

举个生活中的案例,以大排档点餐为例,大排档有已经做好的熟食,此外还可以根据顾客的要求现做,服务员(事件循环)在一个时间段内只能处理一个顾客的点餐请求(事件队列中的任务)。每当一个顾客点完餐,服务员就会检查下菜单,如果顾客点的是熟食(没有阻塞的I/O任务),服务员直接把菜端给客户就行了(直接处理);但是如果顾客点的是现做的食物,那么服务员就选一个空闲的厨师(内部线程池中可用的线程)将顾客的需求转给他,等厨师做好后递给服务员,服务员再端给客户。

为了加深理解,这里有个简单的代码示例:

public class ThreadPoolEventLoop {private final Queue<Runnable> eventQueue = new LinkedBlockingQueue<>(); 创建一个固定大小为3的线程池private final ExecutorService threadPool = Executors.newFixedThreadPool(3); public void startEventLoop() {while (true) {Runnable event;synchronized (eventQueue) {event = eventQueue.poll(); // 从队列中取出事件}if (event != null) {// 判断事件是否是 I/O 阻塞任务if (isIOBound(event)) {threadPool.submit(event); // 使用线程池提交 I/O 阻塞任务} else {event.run(); // 处理非阻塞任务}} else {try {Thread.sleep(100); // 如果没有事件,等待} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}}// 模拟判断事件是否是 I/O 阻塞任务的方法private boolean isIOBound(Runnable event) {// 这里可以根据具体的业务逻辑判断事件是否是 I/O 阻塞任务// 此处简单地假设所有事件都是非阻塞的return false;}public void registerEvent(Runnable event) {synchronized (eventQueue) {eventQueue.offer(event); // 将事件添加到队列尾部}}public static void main(String[] args) {ThreadPoolEventLoop eventLoop = new ThreadPoolEventLoop();// 注册几个简单的事件,其中一个模拟 I/O 阻塞任务eventLoop.registerEvent(() -> System.out.println("Event 1 executed"));eventLoop.registerEvent(() -> System.out.println("Event 2 executed"));eventLoop.registerEvent(() -> System.out.println("Event 3 executed"));// 启动事件循环eventLoop.startEventLoop();}
}//输出
Event 1 executed
Event 2 executed
Event 3 executed

相关文章:

如何用一个例子向10岁小孩解释高并发实时服务的单线程事件循环架构

I/O密集型进程和CPU密集型进程 聊天应用程序、MMO&#xff08;大型多人在线&#xff09;游戏、金融交易系统、等实时服务需要处理大量并发流量和实时数据。 这些服务是I/O密集型的&#xff0c;因为它们花费大量资源处理输入输出操作&#xff0c;例如高吞吐量、低延迟网络通信…...

如何为帕金森病患者选择合适的步行辅助设备?

选择步行辅助设备的步骤和建议 为帕金森病患者选择合适的步行辅助设备时&#xff0c;应考虑以下几个关键因素&#xff1a; 患者的具体症状和需求&#xff1a;帕金森病患者的步行困难可能包括冻结步态、平衡能力下降和肌肉僵硬。选择设备时&#xff0c;应考虑这些症状&#xff…...

【排序算法】1.冒泡排序-C语言实现

冒泡排序&#xff08;Bubble Sort&#xff09;是最简单和最通用的排序方法&#xff0c;其基本思想是&#xff1a;在待排序的一组数中&#xff0c;将相邻的两个数进行比较&#xff0c;若前面的数比后面的数大就交换两数&#xff0c;否则不交换&#xff1b;如此下去&#xff0c;直…...

Unity最新第三方开源插件《Stateful Component》管理中大型项目MonoBehaviour各种序列化字段 ,的高级解决方案

上文提到了UIState, ObjectRefactor等,还提到了远古的NGUI, KBEngine-UI等 这个算是比较新的解决方法吧,但是抽象出来,问题还是这些个问题 所以你就说做游戏是不是先要解决这些问题? 而不是高大上的UiImage,DoozyUI等 Mono管理引用基本用法 ① 添加Stateful Component …...

Spark SQL----INSERT TABLE

Spark SQL----INSERT TABLE 一、描述二、语法三、参数四、例子4.1 Insert Into4.2 Insert Overwrite 一、描述 INSERT语句将新行插入表中或覆盖表中的现有数据。插入的行可以由值表达式指定&#xff0c;也可以由查询结果指定。 二、语法 INSERT [ INTO | OVERWRITE ] [ TABL…...

socket功能定义和一般模型

1. socket的功能定义 socket是为了使两个应用程序间进行数据交换而存在的一种技术&#xff0c;不仅可以使同一个主机上两个应用程序间可以交换数据&#xff0c;而且可以使网络上的不同主机间上的应用程序间进行通信。 2. 图解socket的服务端/客户端模型...

如何在linux中给vim编辑器添加插件

在Linux系统中给Vim编辑器添加插件通常通过插件管理器来完成&#xff0c;以下是一般的步骤&#xff1a; 1.使用插件管理器安装插件 安装插件管理器&#xff08;如果尚未安装&#xff09;&#xff1a; 常见的插件管理器包括 Vundle、vim-plug 和 Pathogen 等。你可以根据个人喜…...

Web 中POST为什么会发送两次请求

文章目录 前言一、浏览器的重试机制二、跨域请求与预检请求三、表单的自动提交四、服务器配置问题五、前端代码的重复执行六、同源策略与CORS总结 前言 我们在做Web开发时&#xff0c;经常会使用浏览器F12查看请求参数是否正确&#xff0c;但是会发现POST请求&#xff0c;一个地…...

C语言经典程序100案例

C语言经典程序100题(完整版) 【程序1】题目&#xff1a;有1、2、3、4个数字&#xff0c;能组成多少个互不相同且无重复数字的三位数都是多少 程序分析&#xff1a;可填在百位、十位、个位的数字都是1、2、3、4。组成所有的排列后再去掉不满足条件的排列。 #include "stdio…...

南京邮电大学统计学课程实验3 用EXCEL进行方差分析 指导

一、实验描述 实验目的 1、学会在计算机上利用EXCEL进行单因素方差分析&#xff1b; 2、学会在计算机上利用EXCEL进行无重复的双因素方差分析。 二、实验环境 实验中使用以下软件和硬件设备 &#xff08;1&#xff09;Windows XP操作系统&#xff1b; &#xff08;2&am…...

2024-07-13 Unity AI状态机2 —— 项目介绍

文章目录 1 项目介绍2 模块介绍2.1 BaseState2.2 ...State2.2.1 PatrolState2.2.2 ChaseState / AttackState / BackState 2.3 StateMachine2.4 Monster 3 其他功能4 类图 项目借鉴 B 站唐老狮 2023年直播内容。 点击前往唐老狮 B 站主页。 1 项目介绍 ​ 本项目使用 Unity 2…...

shell脚本-linux如何在脚本中远程到一台linux机器并执行命令

需求&#xff1a;我们需要从11.0.1.17远程到11.0.1.16上执行命令 实现&#xff1a; 1.让11.0.1.17 可以免密登录到11.0.1.16 [rootlocalhost ~]# ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created d…...

Spring Data Redis + Redis数据缓存学习笔记

文章目录 1 Redis 入门1.1 简介1.2 Redis服务启动与停止&#xff08;Windows&#xff09;1.2.1 服务启动命令1.2.2 客户端连接命令1.2.3 修改Redis配置文件1.2.4 Redis客户端图形工具 2. Redis数据类型2.1 五种常用数据类型介绍 3. Redis常用命令3.1 字符串操作命令3.2 哈希操作…...

在项目中,如何使用springboot+vue+springsecurity+redis缓存+Axios+MySQL数据库+mybatis

要在项目中使用springbootvuespringsecurityredis缓存AxiosMySQL数据库mybatis&#xff0c;可以按照以下步骤进行操作&#xff1a; 创建一个Spring Boot项目&#xff0c;并添加所需的依赖。在pom.xml文件中添加Spring Boot、Spring Security、Redis、MySQL和MyBatis的依赖项。 …...

微调 Florence-2 - 微软的尖端视觉语言模型

Florence-2 是微软于 2024 年 6 月发布的一个基础视觉语言模型。该模型极具吸引力&#xff0c;因为它尺寸很小 (0.2B 及 0.7B) 且在各种计算机视觉和视觉语言任务上表现出色。 Florence 开箱即用支持多种类型的任务&#xff0c;包括: 看图说话、目标检测、OCR 等等。虽然覆盖面…...

【数据结构】二叉树全攻略,从实现到应用详解

​ &#x1f48e;所属专栏&#xff1a;数据结构与算法学习 &#x1f48e; 欢迎大家互三&#xff1a;2的n次方_ ​ &#x1f341;1. 树形结构的介绍 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做…...

微信小程序加载动画文件

最近在做微信小程序的动画&#xff0c;调研了几种方案 PAG 腾讯自家的&#xff0c;分为完整版和lite版&#xff0c;对于矢量动画挺好的&#xff0c;但是位图会有问题 完整版会逐渐卡死&#xff0c;lite虽然不会卡死&#xff0c;但是很模糊&#xff0c;优点是动画文件很的很小。…...

[计算机网络] VPN技术

VPN技术 1. 概述 虚拟专用网络&#xff08;VPN&#xff09;技术利用互联网服务提供商&#xff08;ISP&#xff09;和网络服务提供商&#xff08;NSP&#xff09;的网络基础设备&#xff0c;在公用网络中建立专用的数据通信通道。VPN的主要优点包括节约成本和提供安全保障。 优…...

SQL 中的 EXISTS 子句:探究其用途与应用

目录 EXISTS 子句简介语法 EXISTS 与 NOT EXISTSEXISTS 子句的工作原理实际应用场景场景一&#xff1a;筛选存在关联数据的记录场景二&#xff1a;优化查询性能 EXISTS 与其他 SQL 结构的比较EXISTS vs. JOINEXISTS vs. IN 多重 EXISTS 条件在 UPDATE 语句中使用 EXISTS常见问题…...

OpenSearch分析WAF日志

Web应用防火墙(WAF)是保护web应用程序的重要工具,而分析WAF日志可以帮助我们更好地了解安全威胁并优化防护策略。本文将介绍15个使用OpenSearch分析WAF日志的实用例子,涵盖基础统计、安全分析、性能监控等多个方面。 准备工作 在开始之前,请确保: WAF日志已经被发送到OpenSea…...

2026年福建莆田大平层全屋高端定制选型指南

一、引言福建莆田近年来经济发展迅速&#xff0c;居民生活水平不断提高&#xff0c;大平层住宅逐渐成为高端改善型住房的热门选择。在全屋高端定制方面&#xff0c;消费者面临着众多品牌的选择。本指南旨在为莆田的大平层业主提供一份合规、靠谱且适配自身需求的高端定制品牌选…...

TMS320VC5502PGF300:TI TMS320C55x系列定点DSP,300MHz,176-LQFP封装

TMS320VC5502PGF300&#xff1a;C55x低功耗DSP的300MHz经典音频处理方案在语音识别、音频编解码和通信基带处理等实时信号处理应用中&#xff0c;处理器的能效比&#xff08;单位功耗下的算力&#xff09;往往是系统设计的核心约束。高性能处理器虽然算力强劲&#xff0c;但较高…...

从 AI 工具到音乐生态:可酷加速布局,构建数字音乐全新基础设施

当数字音乐行业从流量竞争迈入生态竞争的新阶段&#xff0c;单一产品的功能边界已难以支撑企业长期增长&#xff0c;完善的生态协同能力逐渐成为企业突围的核心竞争力&#xff0c;也成为定义行业未来格局的关键变量。在此背景下&#xff0c;可酷公司近日对外披露其全新发展战略…...

配置openclaw使用taotoken作为其底层大模型供应商

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 配置 OpenClaw 使用 Taotoken 作为其底层大模型供应商 基础教程类&#xff0c;引导使用 OpenClaw 这类 Agent 框架的开发者&#x…...

会计学论文降AI工具怎么选?财务审计方向高效降重指南

又到了毕业答辩的关键期&#xff0c;不少会计专业的同学都在发愁论文AI率不达标&#xff1a;财务分析部分的数据解读、审计研究的案例推导用AI辅助写完&#xff0c;一检测全是高风险&#xff0c;改了好几遍还是过不了学校的审核。我身边不少师弟师妹踩过工具的坑&#xff0c;要…...

ARM SVE存储指令ST1D与ST1H深度解析与优化

1. ARM SVE存储指令深度解析在ARMv8架构的可扩展向量扩展(SVE)指令集中&#xff0c;ST1D和ST1H指令扮演着关键角色。这些指令专为高效的内存存储操作设计&#xff0c;特别适合处理大规模数据集的场景。与传统的标量存储指令相比&#xff0c;它们能同时处理多个数据元素&#xf…...

Lovable应用性能优化全链路(首屏加载≤300ms实测方案)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;Lovable应用性能优化全链路概览 Lovable 是一款面向高并发、低延迟场景的现代 Web 应用框架&#xff0c;其性能优化需贯穿开发、构建、部署与运行时全生命周期。理解各环节的协同关系与瓶颈传导路径&#xff…...

ETS2LA:卡车模拟游戏中的自动化路径跟随系统如何让你轻松驾驭长途运输?

ETS2LA&#xff1a;卡车模拟游戏中的自动化路径跟随系统如何让你轻松驾驭长途运输&#xff1f; 【免费下载链接】Euro-Truck-Simulator-2-Lane-Assist Plugin based interface program for ETS2/ATS. 项目地址: https://gitcode.com/gh_mirrors/eur/Euro-Truck-Simulator-2-L…...

US Visa Bot:开源智能预约解决方案,告别签证等待焦虑

US Visa Bot&#xff1a;开源智能预约解决方案&#xff0c;告别签证等待焦虑 【免费下载链接】us-visa-bot US Visa Bot 项目地址: https://gitcode.com/gh_mirrors/us/us-visa-bot 您是否曾经为了一个美国签证面试日期而反复刷新页面&#xff0c;却总是错过最佳时机&am…...

3分钟快速上手:Buzz音频转录软件完整使用指南

3分钟快速上手&#xff1a;Buzz音频转录软件完整使用指南 【免费下载链接】buzz Buzz transcribes and translates audio offline on your personal computer. Powered by OpenAIs Whisper. 项目地址: https://gitcode.com/GitHub_Trending/buz/buzz 还在为音频转录烦恼…...