Node.js 异步I/O与事件循环深度优化
Node.js 的核心魅力在于其异步、非阻塞I/O模型,这使得它在处理高并发、I/O密集型应用(如Web服务器、API网关、实时通信服务)时表现出色。然而,这种强大的能力并非凭空而来,它深深植根于其独特的**事件循环(Event Loop)**机制和底层库 Libuv 的协同工作。要真正榨干Node.js的性能潜力,避免常见的性能陷阱,开发者必须深入理解其异步心脏的工作原理。
本文将带你潜入Node.js的底层,剖析事件循环的各个阶段,理解异步I/O是如何被高效处理的,并探讨一系列针对性的优化策略和最佳实践,助你构建出响应更快、吞吐量更高的Node.js应用。
一、Node.js的心脏:事件循环与Libuv
想象一下Node.js是一个技艺高超、但只有一只手的餐厅服务员。他不能同时做多件事情(单线程JavaScript执行),但他非常擅长委托任务。
- 单线程JavaScript: 你的Node.js代码运行在一个主线程上。这意味着任何时候,只有一段JavaScript代码在执行。
- 异步非阻塞I/O: 当遇到耗时的I/O操作(如读取文件、访问数据库、发起网络请求)时,Node.js不会傻等结果。它会将这个任务委托出去。
- Libuv: 这就是Node.js背后的“多手多脚”的厨房团队(通常是一个C++库)。Libuv负责与操作系统交互,处理这些耗时的异步任务。它利用操作系统的原生异步机制(如Linux的epoll, macOS的kqueue, Windows的IOCP)来高效处理网络I/O。对于某些没有原生异步接口的操作(如部分文件系统操作、DNS查询、CPU密集型任务如
zlib
压缩、crypto
的部分操作),Libuv会使用一个线程池 (Thread Pool) 来在后台执行,避免阻塞主JavaScript线程。 - 事件循环 (Event Loop): “服务员”需要一个机制来知道“厨房”何时完成了任务。事件循环就是这个持续运行的协调者。它不断地检查是否有待处理的事件(如I/O完成、定时器到期),并将相应的回调函数放入执行队列,等待主线程空闲时执行。
事件循环的阶段(简化版,但抓住核心):
事件循环并非一个简单的队列,而是一个包含多个阶段的循环过程。了解这些阶段对于理解回调的执行时机至关重要:
- Timers (定时器): 执行
setTimeout()
和setInterval()
设置的回调。注意,它们实际执行的时间可能因系统调度延迟或前面阶段耗时而晚于设定时间。 - Pending Callbacks (待定回调): 执行上轮循环中被推迟的I/O回调(例如,某些系统错误回调)。
- Idle, Prepare (内部使用): Node.js内部使用。
- Poll (轮询): 核心阶段。
- 计算应该阻塞并等待I/O事件的时间。
- 处理轮询队列中的I/O相关回调(几乎所有的I/O回调都在这里执行,除了关闭回调、定时器和
setImmediate
)。 - 如果脚本被
setImmediate()
调度了,或者队列非空,事件循环会结束轮询阶段,进入Check阶段。 - 如果队列为空且没有
setImmediate
:- 如果有
setTimeout
/setInterval
回调准备好了,进入Timers阶段。 - 否则,阻塞在此阶段,等待新的I/O事件进来。
- 如果有
- Check (检查): 执行
setImmediate()
设置的回调。setImmediate
的回调会在Poll阶段完成后立即执行。 - Close Callbacks (关闭回调): 执行一些关闭事件的回调,例如
socket.on('close', ...)
。
微任务 (Microtasks) 与 宏任务 (Macrotasks)
除了上述阶段(主要处理宏任务),Node.js还有微任务队列的概念:
- 宏任务 (Macrotasks): 包括
setTimeout
,setInterval
,setImmediate
, I/O操作的回调等。事件循环的每个阶段处理一类宏任务。 - 微任务 (Microtasks): 包括
process.nextTick()
的回调、Promise的.then()
,.catch()
,.finally()
的回调。 - 执行时机: 极其重要! 在事件循环的每个阶段完成后,以及在处理完一个回调从JavaScript栈返回到事件循环之前,Node.js会立即清空整个微任务队列。如果在执行微任务时又产生了新的微任务,也会在当前阶段内立即执行,直到微任务队列为空,然后才会进入下一个阶段或处理下一个宏任务。
process.nextTick()
的优先级高于Promise微任务。所有nextTick
回调会在所有Promise回调之前执行。
性能启示: 过多的微任务(尤其是process.nextTick
)会阻塞事件循环进入下一阶段,可能导致I/O饥饿或定时器延迟。Promise链过长也可能产生类似效果。
二、异步I/O实战:文件与网络
- 文件I/O:
- 异步方法 (
fs.readFile
,fs.writeFile
等): 推荐使用。它们通常利用Libuv的线程池执行,完成后将回调放入Poll阶段队列。 - 同步方法 (
fs.readFileSync
,fs.writeFileSync
等): 绝对避免在主流程中使用! 它们会彻底阻塞事件循环,直到文件操作完成。只应在程序启动加载配置等极少数场景下考虑。 - 流 (Streams): 对于大文件,使用
fs.createReadStream
和fs.createWriteStream
是最高效的方式。它们允许数据以小块(chunks)的形式流动,避免一次性将整个文件读入内存,并能很好地处理背压(Backpressure)。
- 异步方法 (
- 网络I/O (
http
,net
,dgram
模块): 这是Node.js最高效的部分。通常直接利用操作系统的非阻塞网络I/O(epoll, kqueue, IOCP),无需线程池,性能极好。回调同样在Poll阶段处理。
三、性能杀手:阻塞事件循环
Node.js 的性能命脉在于保持事件循环的畅通。任何长时间占用CPU、阻止事件循环快速迭代的操作都是性能杀手。
常见阻塞场景:
- CPU密集型同步计算: 复杂的数学运算、图像处理(未使用Worker)、加密/解密(未使用异步API或Worker)、深度递归且无优化的函数等。
// Bad: Blocking calculation function fibonacci(n) {if (n <= 1) return n;return fibonacci(n - 1) + fibonacci(n - 2); // Extremely inefficient for large n } // If called with a large number, it blocks the event loop for a long time. const result = fibonacci(45);
- 低效的正则表达式: 某些正则表达式(特别是包含复杂的回溯或嵌套量词)在处理特定输入时可能导致灾难性的性能(ReDoS - Regular Expression Denial of Service)。
- 大型JSON的同步解析/序列化:
JSON.parse()
和JSON.stringify()
是同步操作。处理非常大的JSON对象(几十MB甚至更大)会阻塞事件循环。 - 同步的I/O操作: 如前所述,
fs.readFileSync
等。 - 未优化的循环或算法: O(n²) 或更高复杂度的算法在处理大数据集时。
后果: 阻塞期间,Node.js无法处理新的客户端请求、无法响应已完成的I/O操作、无法执行定时器等。表现为:延迟急剧增加、吞吐量骤降、应用无响应。
四、优化策略:让Node.js飞起来
1. 坚决编写非阻塞代码
- 拥抱异步: 优先使用异步API(回调、Promise、Async/Await)。
- 识别并消除同步瓶颈: 使用性能分析工具(后面介绍)定位长时间运行的同步代码。
2. 高效利用异步模式
Promise.all
vs 顺序await
: 对于互不依赖的异步操作,使用Promise.all
并行执行,而不是用await
一个接一个地等待。// Bad: Sequential awaits async function fetchData() {const user = await db.getUser(userId);const posts = await db.getPosts(userId); // Waits for getUser to finishreturn { user, posts }; // Total time = time(getUser) + time(getPosts) }// Good: Parallel execution async function fetchDataOptimized() {const [user, posts] = await Promise.all([db.getUser(userId),db.getPosts(userId) // Starts concurrently with getUser]);return { user, posts }; // Total time ≈ Math.max(time(getUser), time(getPosts)) }
- 谨慎使用
async/await
内部的循环: 如果循环体内有await
,每次迭代都会暂停并等待,本质上是串行的。考虑是否可以先收集所有异步操作的Promise,然后用Promise.all
一次性等待。
3. 驾驭流 (Streams) 的力量
- 处理大数据: 对于大文件读写、网络数据传输,流是首选。它内存占用小,响应及时。
pipe()
处理背压: 使用.pipe()
方法连接可读流和可写流。pipe
会自动处理背压——当写入流处理不过来时,它会暂停读取流,防止内存爆掉。这是手动处理流数据时容易忽略的关键点。const fs = require('fs'); const http = require('http');http.createServer((req, res) => {// Efficiently pipe large file to response, handling backpressureconst readStream = fs.createReadStream('/path/to/large/video.mp4');readStream.pipe(res);readStream.on('error', (err) => {res.statusCode = 500;res.end('Server error');});res.on('error', (err) => {// Client connection closed prematurely, etc.readStream.destroy();}); }).listen(8080);
4. 解放主线程:Worker Threads
- 适用场景:CPU密集型任务。 对于那些无法避免的、会阻塞事件循环的计算任务(如图像处理、复杂计算),使用
worker_threads
模块将它们放到单独的线程执行。 - 工作方式: 主线程创建Worker,通过消息传递(
postMessage
,on('message')
)与Worker通信。Worker执行计算,将结果发回主线程。 - 注意: Worker线程的创建和通信有开销。不适合用于I/O密集型任务(Node.js的异步I/O已经很高效了),也不适合非常小的计算任务。
// main.js
const { Worker } = require('worker_threads');function runHeavyTask(data) {return new Promise((resolve, reject) => {const worker = new Worker('./worker.js', { workerData: data });worker.on('message', resolve);worker.on('error', reject);worker.on('exit', (code) => {if (code !== 0) reject(new Error(`Worker stopped with exit code ${code}`));});});
}async function handleRequest(req, res) {const result = await runHeavyTask({ input: req.query.data });res.send({ result });
}// worker.js
const { workerData, parentPort } = require('worker_threads');// Perform CPU-intensive calculation
const result = performHeavyCalculation(workerData.input);
parentPort.postMessage(result);function performHeavyCalculation(input) { /* ... CPU-bound logic ... */ }
5. 优化I/O操作本身
- 数据库连接池: 避免为每个请求创建新的数据库连接,使用连接池复用连接。
- 缓存: 对于频繁访问且不经常变化的数据(如配置、热点用户信息),使用内存缓存(如Redis、Memcached,或Node内存缓存库)减少对数据库或外部服务的访问。
- 批量操作: 如果需要对数据库或外部API进行多次类似的操作,看是否支持批量接口(如
INSERT INTO ... VALUES (...), (...), ...
),减少网络往返次数。 - 超时与重试: 合理设置对外部服务的请求超时,并实现适当的重试逻辑(如指数退避),避免长时间等待失败的请求。
五、诊断工具:透视性能瓶颈
- Node.js 内置 Profiler (
--prof
): 运行node --prof your_script.js
,生成 V8 profiler 日志文件。使用node --prof-process <isolate-*.log>
分析日志,找出CPU耗时热点函数。 - Chrome DevTools for Node.js: 使用
node --inspect
或node --inspect-brk
启动应用,然后在 Chrome 浏览器中打开chrome://inspect
连接。可以使用 Performance 面板录制CPU Profile,Memory 面板进行内存分析。 perf_hooks
模块: 提供API来测量代码执行时间,如performance.now()
,performance.mark()
,performance.measure()
。async_hooks
模块: 非常底层的API,用于追踪异步资源的生命周期。可以用来构建更复杂的性能监控或诊断工具,但使用复杂。- Clinic.js (开源工具集): 包含 Doctor, Flame, Bubbleprof 等工具,提供可视化诊断,帮助发现 I/O 问题、事件循环延迟、CPU 热点等。
六、结语:理解是优化的前提
Node.js 的高性能异步模型并非魔法,而是建立在精心设计的事件循环和 Libuv 之上。深度优化 Node.js 应用性能的关键在于:
- 深刻理解事件循环机制: 知道代码何时执行,微任务与宏任务的交互。
- 识别并消除阻塞: 保持事件循环的畅通是第一要务。
- 高效利用异步原语: 选择合适的异步模式(Promise.all, Streams)。
- 分流CPU密集任务: 合理使用 Worker Threads。
- 优化外部交互: 关注数据库、缓存和API调用。
- 善用诊断工具: 用数据说话,定位真正的瓶颈。
掌握了这些原理和实践,你就能更自信地驾驭Node.js的异步力量,构建出真正高性能、高并发的后端服务。性能优化是一个持续的过程,保持好奇心,不断学习和测量,你的Node.js应用将能应对更大的挑战。
相关文章:
Node.js 异步I/O与事件循环深度优化
Node.js 的核心魅力在于其异步、非阻塞I/O模型,这使得它在处理高并发、I/O密集型应用(如Web服务器、API网关、实时通信服务)时表现出色。然而,这种强大的能力并非凭空而来,它深深植根于其独特的**事件循环(…...
npm 常用操作和配置
一、npm 核心操作 1. 初始化项目 npm init # 交互式创建 package.json npm init -y # 跳过提问,直接生成默认 package.json2. 安装依赖 npm install <package> # 安装包到本地 node_modules(生产依赖) npm in…...
嵌入式芯片中的 低功耗模式 内容细讲
电源域与运行级别概述 电源域(Power Domain) 核心域(Core Domain):包括 CPU 核心和关键架构模块(如 NVIC、CPU 内核寄存器)。 外设域(Peripheral Domain):…...
React-请勿在循环或者条件语句中使用hooks
这是React Hooks的首要规则,这是因为React Hooks 是以单向循环链表的形式存储,即是有序的。循环是为了从最后一个节点移到一个节点的时候,只需通过next一步就可以拿到第一个节点,而不需要一层层回溯。React Hooks的执行࿰…...
React-memo (useMemo, useCallback)
在react中,当我们setState之后,若值发生变化,则会重新render当前组件以及其子组件 (默认情况下),在必要的时候,我可使用memo (class组件则对应shouldComponentUpdate、PureComponent)进行优化,来减少无效渲…...
点云数据处理开源C++方案
一、主流开源库对比 库名称特点适用场景开源协议活跃度PCL功能最全,算法丰富科研、工业级应用BSD★★★★★Open3D现代API,支持Python绑定快速开发、深度学习MIT★★★★☆CGAL计算几何算法强大网格处理、高级几何运算GPL/LGPL★★★☆☆PDAL专注于点云…...
android测试依赖
Android 项目中常用的测试相关库 1. androidx.arch.core:core-testing:2.2.0 作用: 提供与 Android Architecture Components(如 LiveData、ViewModel)相关的测试工具。主要用于测试基于 LiveData 的异步操作。 常见功能: 即时…...
Gradle与Idea整合
文章目录 1. Groovy 简介2. Groovy 安装[非必须]3. 在idea中创建java工程 1. Groovy 简介 在某种程度上,Groovy可以被视为Java的一种脚本化改良版,Groovy也是运行在JVM上,它可以很好地与Java代码及其相关库进行交互操作。它是一种成熟的面向对象编程语言…...
【数据结构】励志大厂版·初阶(复习+刷题)单链表
前引:此篇文章作为小编复习的记录,将快速回忆单链表的知识点,讲解单链表增删查找的实现,每个细节之处要注意的地方,解释为何这样设计。文章末尾包含了单链表算法题, 同样解释详细,借助题目再次巩…...
前端面试宝典---参数解构+默认值的面试题
重点要义 对于函数参数要解构,且参数有默认值的,一律用Object.assign的思路去合并参。 看不懂这句话没关系,看下面的例子\ 例子1 function fn ({ x 1, y } { y: 10 }) {console.log(x, y) } fn() // 1 10没有传递实参,你就把{ …...
【开发心得】筑梦上海:项目风云录(16)
目录 代码反面案例 李青与诺基亚的兴衰 并行项目下的利益纠葛与团队协作 未完待续。。。 今天分享的是一个反面案例,也算是一个避坑指南了。 代码反面案例 今天分享的代码是一个反面案例,当时由于项目人员变动频繁,经常是新人看不太懂旧…...
Neovim插件深度解析:mcphub.nvim如何用MCP协议重构开发体验
在AI与工具链深度融合的今天,Neovim 作为现代开发者的生产力工具,正通过插件生态不断突破边界。mcphub.nvim 作为一款基于 MCP(Model Context Protocol) 协议的插件,重新定义了Neovim与智能工具的交互方式。它不仅简化了MCP服务器的集成与管理,更通过直观的UI和生态整合,…...
Qt UDP 通信的详细实现步骤和示例代码
在 Qt 中实现 UDP 通信主要使用 QUdpSocket 类。以下是 UDP 通信的详细实现步骤和示例代码: 一、UDP 通信基础 无连接协议:不需要建立持久连接数据报模式:以独立数据包(datagram)形式发送适用场景:实时性要…...
(二)Trae 配置C++ 编译
Trae配置c编译 零 CMake 编译C0.1 下载安装0.2 安装设置0.3 三种编译方式(见 下文 一 二 三)0.4 调试 (见 下文四) 一 使用MSVC方式编译1.1 安装编译环境1.2安装插件1.3 设置文件 二 使用GCC方式2.1 安装编译环境2.1.1下载:[MinGw](https://gcc-mcf.lhmouse.com/)2.1.2安装:(以…...
动态规划算法的欢乐密码(一):斐波那契数模型
专栏:算法的魔法世界 个人主页:手握风云 目录 一、动态规划 二、例题讲解 2.1. 第 N 个泰波那契数 2.2. 三步问题 2.3. 使用最小花费爬楼梯 2.4. 解码方法 一、动态规划 动态规划是一种将复杂问题分解为更小的子问题,并利用子问题的解来…...
【FreeRTOS进阶】优先级翻转现象详解及解决方案
【FreeRTOS进阶】优先级翻转现象详解及解决方案 接下来我们聊聊优先级翻转这个经典问题。这个问题在实时系统中经常出现,尤其是在任务较多的场景下,而且问题定位起来比较麻烦。 什么是优先级翻转? 优先级翻转的核心定义很简单:…...
解决 IntelliJ IDEA 项目启动时端口冲突问题
1.问题 Description: The Tomcat connector configured to listen on port 8082 failed to start. The port may already be in use or the connector may be misconfigured. Action: Verify the connectors configuration, identify and stop any process thats listening…...
笔试专题(十一)
文章目录 添加字符(暴力枚举)题解代码 城市群数量(dfs)题解代码 判断是不是平衡二叉树(递归)题解代码 最大子矩阵(二维前缀和)题解代码 小葱的01串 (固定区间大小的滑动窗…...
C++11新增语法:列表初始化
前言: 接下来我们将要讲解,相较于c98,c11中新增的语法以及如何使用~。我们首先来讲解:列表初始化。 下文预告:右值引用和移动语义 C98中传统的{} 在c98中的{},仅能初始化数组和结构体 #include<iostrea…...
Linux:基础IO---动静态库
文章目录 1. 动静态库前置知识1.1 动静态库知识回顾1.2 什么是动静态库 2. 动静态库2.1 站在库的制作者的角度2.2 站在库的使用者的角度2.3 动态库是怎么被加载的(原理) 序:上一篇文章我们从认识到理解,从理解到实现场景ÿ…...
从裸仓库到GitLab全解析
Git服务器搭建与使用指南:从裸仓库到GitLab全解析 前言 在团队协作开发中,版本控制系统是必不可少的工具。虽然GitHub提供了优秀的代码托管服务,但企业级项目往往需要更安全的私有化部署方案。本文将手把手教你两种主流的Git服务器搭建方式…...
OzGIS:地理信息分析与处理软件
大家好,今天为大家介绍的软件是OzGIS:一款地理信息分析与处理软件。下面,我们将从软件的主要功能、支持的系统、软件官网等方面对其进行简单的介绍。 OzGIS官网网址为:https://ozgis.sourceforge.io/。 OzGIS是一款开源软件&#…...
PHP异常处理__Throwable
在 PHP 里,Throwable 是一个极为关键的接口,自 PHP 7 起被引入。它为错误和异常处理构建了一个统一的框架。下面会详细介绍 Throwable 的相关内容。 1. 基本概念 Throwable 是 Exception 和 Error 的父接口。在 PHP 7 之前,异常(…...
PHP异常处理__Exception类
以下是对 PHP 中 Exception 类的详细解释: 一、Exception 类概述 Exception 是 PHP 中所有异常类的基类。它提供了一个通用的异常处理机制,用于处理程序执行过程中可能出现的错误情况。当程序中出现异常时,可以创建 Exception 的实例并将其…...
C++中动态多态类别浅析
非抽象类继承和虚函数 #include <iostream> using namespace std;class Base { public:virtual void func() { // 虚函数,支持动态绑定cout << "Base::func()" << endl;} };class Derived : public Base { public:void func() overrid…...
游戏引擎学习第234天:实现基数排序
回顾并为今天的内容设定背景 我们今天继续进行排序的相关,虽然基本已经完成了,但还是想收尾一下,让整个流程更完整。其实这次排序只是个借口,主要是想顺便聊一聊一些计算机科学的知识点,这些内容在我们项目中平时不会…...
系分架构论文《论高并发场景的架构设计和开发方法》
系统分析师论文范文系列 【摘要】 2022年8月,我司承接了某知名电商平台“秒杀系统架构优化”项目,我作为系统分析师主导了整体架构设计与技术选型工作。该平台在促销活动中面临瞬时流量超过50万QPS的挑战,原有架构存在数据库崩溃、服务响应延…...
最新得物小程序sign签名加密,请求参数解密,响应数据解密逆向分析
点击精选,出现https://app.dewu.com/api/v1/h5/index/fire/index 这个请求 直接搜索sign的话不容易定位 直接搜newAdvForH5就一个,进去再搜sign,打上断点 可以看到t.params就是没有sign的请求参数, 经过Object(a.default)该函数…...
jangow靶机笔记(Vulnhub)
环境准备: 靶机下载地址: https://download.vulnhub.com/jangow/jangow-01-1.0.1.ova kali地址:192.168.144.128 靶机(jangow)地址:192.168.144.180 一.信息收集 1.主机探测 使用arp-scan进行主机探…...
Spring Boot + Caffeine:打造高性能缓存解决方案
1. 引言 1.1 缓存的重要性 缓存是提升系统性能的关键技术之一,通过将频繁访问的数据存储在内存中,减少对数据库或其他外部系统的访问次数,从而降低延迟并提高吞吐量。 缓存的基本概念:缓存是一种临时存储机制,用于快速访问常用数据。缓存在提升系统性能中的作用:减少数…...
C++入门小馆: 深入string类
嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的pa…...
命令行基础
学习目标 掌握VRP命令行的基础知识 利用VRP命令行进行基本的配置 VRP命令行的基础知识 一、VRP 命令行基本架构 1. 用户视图(User View) 进入方式:设备启动后默认进入,提示符为 <HUAWEI>。功能&#…...
10-DevOps-Jenkins参数化构建实现多版本发布
在之前的Jenkins配置中,固定写死了程序的版本号,实际情况是随着版本的不断迭代,版本号也是不断变化的,版本号由代码仓库(GitLab)设置。 当前Jenkins配置是固定写的1.0,本节我们要把它改成动态的…...
C++游戏服务器开发之⑦redis的使用
目录 1.当前进度 2.守护进程 3.进程监控 4.玩家姓名添加文件 5.文件删除玩家姓名 6.redis安装 7.redis存取命令 8.redis链表存取 9.redis程序结构 10.hiredisAPI使用 11.基于redis查找玩家姓名 12.MAKEFILE编写 13.游戏业务实现总结 1.当前进度 2.守护进程 3.进程监…...
二进制裁剪命令mips-linux-gnu-strip 命令的使用
-s 或者--strip-all:移除所有符号和调试信息 -g 或者--strip-debug:仅移除调试信息 -d 或者--strip-unneeded:移除不需要的符号 默认不传任何参数 也是移除所有符号和调试 应用:把文件系统所有二进制镜像使用一遍,缩小文件系统大小 79K Apr 19 15:47 fat.ko //使用前 mips-l…...
【Bluedroid】蓝牙存储模块配置管理:启动、读写、加密与保存流程解析
本文围绕蓝牙存储模块展开,主要解析了蓝牙存储模块(StorageModule)的初始化流程,重点围绕配置文件校验、读取、设备类型修复及加密处理展开。通过工厂重置检测、校验和验证、多源配置加载、设备类型推断修正等步骤,确保…...
SpringBoot启动后初始化的几种方式
目录 一、静态代码块 二、构造方法 三、PostConstruct 四、InitializingBean 接口 五、 Bean 注解中的 initMethod 六、 CommandLineRunner 接口 七、ApplicationRunner 接口 八、EventListener事件 九、SmartInitializingSingleton接口 十、ApplicationListener接口…...
asp.net core webapi+efcore
简洁的restfull风格 目前c#提供了多种风格的web编程,因为微软有自己的前端,所以集成了很多内容,不过基于现在编程前后端分离的模式,webapi是合适的。 webapi 目前网络上有很多介绍,不反复说这个了。在建立控制器时&…...
java怎么完善注册,如果邮箱中途更换,能否判断
解析在下面 附赠代码 private static class CodeInfo {String code;long timestamp;CodeInfo(String code, long timestamp) {this.code code;this.timestamp timestamp;}}// 存储验证码(邮箱 -> 验证码信息)(保证线程安全) 以免中途更改邮箱pri…...
实战设计模式之备忘录模式
概述 与解释器模式、迭代器模式一样,备忘录模式也是一种行为设计模式。备忘录模式允许我们保存一个对象的状态,并在稍后恢复到这个状态。该模式非常适合于需要回滚、撤销或历史记录等功能的应用场景。通过使用备忘录模式,开发者可以轻松添加诸…...
数据库表设计
一对一关系 共享主键 两个表的主键是相同的 唯一外键 从表中记录主表的id 一对多关系 从表(多的表)存储主表的id 多对多关系 设计一个中间表(关联表),它有两列分别记录两个主表(A 和 B)…...
Linux 桌面环境 LXQt 2.2 发布
Linux 桌面环境 LXQt 2.2 于 2025 年 4 月 17 日正式发布。这是该轻量级开源 Qt 桌面环境的最新稳定版本,带来了诸多改进,特别是在 Wayland 支持方面。以下是一些主要的更新内容: Wayland 支持增强: 提升了多屏支持,使…...
多人五子棋联机对战平台 测试报告
目录 项目介绍 测试用例设计 部分功能测试示例 自动化测试 测试范围 排除范围 自动化测试目录编辑 执行全部自动化测试用例 性能说明 总结 性能测试 结果分析 测试总结 项目介绍 该项目基于WebSocket实现实时通信,采用SSM框架构建在线五子棋多人联机…...
探索 .bat 文件:自动化任务的利器
在现代计算机操作中,批处理文件(.bat 文件)是一种简单而强大的工具,它可以帮助我们自动化重复性任务,工作效率提高。尽管随着编程语言和脚本工具的发展,.bat 文件的使用频率有所下降,但它依然是…...
240419 leetcode exercises
240419 leetcode exercises jarringslee 文章目录 240419 leetcode exercises[19. 删除链表的倒数第 N 个结点](https://leetcode.cn/problems/remove-nth-node-from-end-of-list/)🔁 经典方法:两次遍历暴力求解🔁 双指针法 :快慢…...
开源Midjourney替代方案:企业级AI绘画+PPT生成系统+AI源码
「AI取代设计师?」开源Midjourney替代方案:企业级AI绘画PPT生成系统 ——零代码私有化部署,5倍速出图100%版权合规 设计师行业的危机与机遇 1. 传统设计流程的致命短板 痛点 人工设计 AI系统 单张海报耗时 3小时(含反复修改…...
学习笔记十七——Rust 支持面向对象编程吗?
🧠 Rust 支持面向对象编程吗? Rust 是一门多范式语言,主要以 安全、并发、函数式、系统级编程为核心目标,但它同时也支持面向对象的一些关键特性,比如: 特性传统 OOP(如 Java/C)Ru…...
图灵奖得主LeCun:DeepSeek开源在产品层是一种竞争,但在基础方法层更像是一种合作;新一代AI将情感化
图片来源:This is World 来源 | Z Potential Z Highlights: 新型的AI系统是以深度学习为基础,能够理解物理世界并且拥有记忆、推理和规划能力的。一旦成功构建这样的系统,它们可能会有类似情感的反应,但这些情感是基…...
Flink框架十大应用场景
Flink框架适合应用的场景 1. 流式数据处理 Flink框架最常用的应用场景是流式数据处理。流式数据处理是指对实时数据进行处理,以便及时地做出决策。例如,一个电商网站需要对用户的行为进行实时分析,以便根据用户的兴趣和行为推荐商品。Flink框架可以帮助电商网站实时地处理数…...
C++镌刻数据密码的树之铭文:二叉搜索树
文章目录 1.二叉搜索树的概念2.二叉搜索树的实现2.1 二叉搜索树的结构2.2 二叉搜索树的节点寻找2.2.1 非递归2.2.2 递归 2.3 二叉搜索树的插入2.3.1 非递归2.3.2 递归 2.4 二叉搜索树的删除2.4.1 非递归2.4.2 递归 2.5 二叉搜索树的拷贝 3.二叉树的应用希望读者们多多三连支持小…...