說一說Node.js高性能開發中的I/O操作

眾所周知,在軟件開發的領域中,輸入輸出(I/O)操作是程序與外部世界交互的重要環節,比如從文件讀取數據、向網絡發送請求等。這段時間,也指導項目中一些項目的開發工作,發現在Node.js運用中,理解阻塞與非阻塞 I/O 的差異,是掌握 Node.js 這一強大開發工具的關鍵所在。之所以這么說,是我發現目前項目組大多開發人員(剛畢業不久)對此知識點沒有完整的認知,所以在大多數現代 Web 應用中,非阻塞 I/O 憑借其出色的性能和可擴展性,還有更大的提升空間。所以今天結合者Node.js這一個話題將這個概念融合在一起說說。

一、I/O 操作的基本概念

先了解一下,基本概念。I/O 操作指的是計算機系統與外部設備(如硬盤、網絡接口、鍵盤、顯示器等)之間的數據傳輸過程。在程序運行過程中,I/O 操作往往是相對緩慢的,與 CPU 的高速運算形成鮮明對比。例如,從硬盤讀取一個大文件可能需要幾十毫秒,而 CPU 在這段時間內可以執行數百萬條指令。因此,如何高效地處理 I/O 操作,對于程序的性能至關重要。

二、阻塞 I/O:傳統的同步處理方式

阻塞 I/O 是一種傳統的同步處理方式。當程序執行阻塞 I/O 操作時,會一直等待該操作完成,在這期間程序會被 "阻塞",無法執行其他任何任務。就好比你去銀行辦理業務,取號后必須在大廳等待叫號,在等待的過程中,你不能做其他任何事情,只能眼巴巴地等著。

以讀取文件為例,在使用阻塞 I/O 的編程語言中(如傳統的同步 Java I/O),當程序調用讀取文件的函數時,會立即進入等待狀態,直到文件數據被完全讀取到內存中,程序才會繼續執行后續的代碼。在等待的過程中,CPU 資源被浪費,因為程序無法利用這段時間去處理其他任務。

阻塞 I/O 的優點是編程模型簡單,易于理解和調試。開發人員可以按照順序編寫代碼,不需要考慮異步回調等復雜的邏輯。然而,它的缺點也非常明顯,尤其是在處理多個 I/O 操作時,性能會急劇下降。例如,當一個服務器需要同時處理多個客戶端的請求時,如果每個請求都采用阻塞 I/O 方式,服務器必須為每個請求創建一個獨立的線程或進程來處理,這會導致系統資源的大量消耗,并且線程或進程之間的上下文切換也會帶來額外的開銷。

三、非阻塞 I/O:異步處理的革新

非阻塞 I/O 是一種異步處理方式,它允許程序在發起 I/O 操作后,不需要立即等待操作完成,而是繼續執行后續的代碼。當 I/O 操作完成后,系統會以某種方式通知程序(如回調函數、事件驅動等),程序再處理 I/O 操作的結果。這就好比你在網上購物時下單后,不需要一直盯著訂單狀態,而是可以去做其他事情,等快遞到達時,快遞員會打電話通知你。

在非阻塞 I/O 模型中,程序通過輪詢或者事件驅動的方式來管理多個 I/O 操作輪詢是指程序定期檢查 I/O 操作是否完成,這種方式雖然簡單,但會浪費 CPU 資源,因為在輪詢的過程中,CPU 需要不斷地執行檢查操作。事件驅動則是一種更高效的方式,它通過操作系統提供的事件機制,當 I/O 操作完成時,自動觸發相應的事件,程序只需注冊事件處理函數即可。

Node.js 就是基于事件驅動和非阻塞 I/O 構建的運行環境。它采用單線程的事件循環機制,能夠高效地處理大量的并發 I/O 操作。在 Node.js 中,當程序發起一個 I/O 操作(如讀取文件、處理網絡請求等)時,會將該操作交給底層的操作系統去處理,然后立即返回,繼續執行后續的代碼。當操作系統完成 I/O 操作后,會將結果放入事件隊列中,Node.js 的事件循環會不斷地從事件隊列中取出事件,并執行相應的回調函數來處理結果

四、阻塞與非阻塞 I/O 的核心差異

1、程序執行方式:阻塞 I/O 會阻塞程序的執行,直到 I/O 操作完成;非阻塞 I/O 不會阻塞程序的執行,程序可以在發起 I/O 操作后繼續執行其他任務。

2、資源利用:阻塞 I/O 需要為每個 I/O 操作創建獨立的線程或進程,導致系統資源的大量消耗;非阻塞 I/O 通過單線程和事件驅動的方式,高效地利用系統資源,能夠處理大量的并發請求。

3、并發性:阻塞 I/O 的并發性較差,難以處理高并發的場景;非阻塞 I/O 具有良好的并發性,能夠輕松應對大量的并發 I/O 操作。

4、編程模型:阻塞 I/O 的編程模型簡單,易于理解;非阻塞 I/O 的編程模型相對復雜,需要處理異步回調、Promise、async/await 等異步編程模式。

五、Node.js 中非阻塞 I/O 的實現原理

前面鋪墊了概念知識,現在就講其重點?Node.js 的非阻塞 I/O 實現主要依賴于底層的 libuv 庫。libuv 是一個跨平臺的異步 I/O 庫,它提供了事件循環、文件 I/O、網絡 I/O、子進程等功能。在 Windows 系統上,libuv 使用 IOCP(輸入輸出完成端口)來實現異步 I/O;在 Linux 系統上,使用 epoll;在 macOS 系統上,使用 kqueue。這些底層的異步 I/O 機制能夠高效地管理大量的并發 I/O 操作。可以說是各取所長,完成相應的I/O操作。

Node.js 的事件循環是其核心機制,它負責處理事件隊列中的事件,并執行相應的回調函數。事件循環分為多個階段,包括定時器階段(timers)、I/O 回調階段(I/O callbacks)、空閑 / 準備階段(idle/prepare)、輪詢階段(poll)、檢查階段(check)和關閉回調階段(close callbacks)。每個階段都有其特定的功能,例如定時器階段處理 setTimeout 和 setInterval 設置的回調函數,輪詢階段處理新的 I/O 事件等。

六、非阻塞 I/O 在現代 Web 應用中的優勢

1、高性能:非阻塞 I/O 能夠高效地處理大量的并發請求,減少系統資源的消耗,提高服務器的吞吐量。例如,一個使用 Node.js 構建的 Web 服務器,可以輕松處理數萬個并發連接,而傳統的阻塞 I/O 服務器在處理大量并發連接時,性能會急劇下降。

2、可擴展性:非阻塞 I/O 的編程模型使得應用程序更容易擴展。開發人員可以通過添加更多的事件處理函數來處理更多的并發請求,而不需要擔心線程或進程數量過多帶來的問題。

3、內存占用低:由于 Node.js 采用單線程的事件循環機制,不需要為每個請求創建獨立的線程或進程,因此內存占用較低,能夠在有限的硬件資源下運行更多的應用程序。

4、適合 I/O 密集型應用現代 Web 應用通常是 I/O 密集型的,例如需要處理大量的網絡請求、文件讀取等操作。非阻塞 I/O 在處理這些操作時具有明顯的優勢,能夠充分利用系統資源,提高應用程序的性能。

七、阻塞 I/O 的適用場景

雖然非阻塞 I/O 在大多數情況下表現更好,但阻塞 I/O 在某些特定場景下仍然有其用武之地。例如,當處理少量的 I/O 操作,并且這些操作的執行時間非常短,此時阻塞 I/O 的開銷可能可以忽略不計,而其簡單的編程模型可能更適合開發。此外,在一些需要嚴格順序執行的場景中,阻塞 I/O 也可能是一個不錯的選擇。

最后小結以下

阻塞與非阻塞 I/O 是兩種不同的 I/O 處理方式,它們各有優缺點。阻塞 I/O 編程簡單,但性能和可擴展性較差,適合處理少量的、簡單的 I/O 操作;非阻塞 I/O 編程相對復雜,但具有高性能和良好的可擴展性,適合處理大量的并發 I/O 操作。Node.js 作為基于非阻塞 I/O 和事件驅動的運行環境,充分發揮了非阻塞 I/O 的優勢,成為了現代 Web 應用開發的重要選擇。

無論是專業人士還是非專業人士,理解阻塞與非阻塞 I/O 的差異對于掌握 Node.js 和構建高性能的 Web 應用都具有重要意義。對于專業人士來說,深入理解其底層實現原理和編程模型,能夠更好地利用 Node.js 的優勢進行開發和優化;這也是?Node.js 在現代 Web 開發中如此受歡迎的原因之一吧。隨著 Web 應用的不斷發展,對高性能和可擴展性的要求越來越高,非阻塞 I/O 將在未來的開發中發揮更加重要的作用。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/80913.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/80913.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/80913.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Charles抓包并破解ProtoBuf請求

安裝Charles并抓包 如果是外網的需要root安裝一系列證書等,詳細見參考文章: 在雷電模擬器安卓7.0上使用Charles抓包詳細教程 遇到如下問題: 1.粘貼到目錄/system/etc/security/cacerts內,粘貼不了。需要打開這個 2.模擬器wifi打…

Odoo 18 安全組與訪問權限管理指南

Odoo 18 安全組與訪問權限管理指南 一、準備工作:在自定義模塊中創建安全配置文件 創建 security 文件夾 在自定義模塊內創建名為 security 的文件夾,用于存放安全組和訪問權限的定義文件。 二、定義模型訪問權限:ir.model.access.csv 文…

使用lldb查看Rust不同類型的結構

目錄 前言 正文 標量類型 復合類型——元組 復合類型——數組 函數 &str struct 可變數組vec Iter String Box Rc Arc RefCell Mutex RwLock Channel 總結 前言 筆者發現這個lldb挺好玩的,可以查看不同類型的結構,雖然這好像是C的東…

uniapp使用ui.request 請求流式輸出

正文: 在現代Web開發中,實時數據流和長時間運行的請求變得越來越常見,尤其是在處理大量數據或進行實時通信時。在這種情況下,uniapp 提供的 ui.request 請求方法可以幫助我們輕松實現流式輸出請求。本文將介紹如何使用 uni.reques…

如何恢復被勒索軟件加密的服務器文件(解密與備份策略)

針對勒索軟件加密文件的恢復和解密策略,結合當前數據安全最佳實踐,整理應對指南如下: 一、文件解密與修復方法 立即隔離設備? 斷開網絡連接并禁用共享功能,防止病毒橫向傳播 通過文件后綴異常(如.locked、.wxx&…

JS,ES,TS三者什么區別

Java Script(JS)、ECMAScript(ES)、TypeScript(TS) 的核心區別與關聯的詳細解析,結合技術背景、設計目標及應用場景展開說明: 一、核心定義與關系 JavaScript(JS) 定義:一種動態類型、基于原型的腳本語言,由 Netscape 公司于 1995 年首次開發,用于網頁交互功能。角…

【MapReduce入門】深度解析MapReduce:定義、核心特點、優缺點及適用場景

目錄 1 什么是MapReduce? 2 MapReduce的核心特點 2.1 分布式處理 2.2 容錯機制 3 MapReduce的完整工作流程 4 MapReduce的優缺點分析 4.1 優勢 4.2 局限性 5 MapReduce典型應用場景 5.1 適用場景 5.2 不適用場景 6 MapReduce與其他技術的對比 7 總結 1…

【Redis】分布式鎖的實現

目錄 一、本地鎖存在的問題 二、redis實現分布式鎖原理 三、使用示例 四、鎖誤刪問題 解決思路 獲取鎖和釋放鎖代碼優化 五、鎖釋放的原子性問題 解決思路(Lua腳本) 使用流程 總結 大家好,我是千語。上期給大家講了使用悲觀鎖來解決…

Unity3D對象池設計與實現詳解

前言 在Unity3D中,對象池(Object Pooling)是一種優化技術,用于減少頻繁實例化和銷毀對象帶來的性能開銷。以下是對象池的詳細設計和實現步驟: 對惹,這里有一個游戲開發交流小組,希望大家可以點…

[Spring]-組件的生命周期

組件生命周期 認識組件的聲明周期 實驗1 通過Bean指定組件的生命周期 package com.guigu.spring.ioc.bean;Data public class User {private String username;private String password;private Car car;Autowiredpublic void setCar(Car car) {System.out.println("自動…

【golang】網絡數據包捕獲庫 gopacket

詳解 github.com/google/gopacket/pcap 包 github.com/google/gopacket/pcap 是 Go 語言中一個強大的網絡數據包捕獲庫,它是 gopacket 項目的一部分,提供了對 libpcap(Linux/Unix)和 WinPcap(Windows)的 G…

RBTree的模擬實現

1:紅黑樹的概念 紅?樹是?棵?叉搜索樹,他的每個結點增加?個存儲位來表?結點的顏?,可以是紅?或者??。通過對任何?條從根到葉?的路徑上各個結點的顏?進?約束,紅?樹確保沒有?條路徑會?其他路徑?出2倍,因…

React 第三十九節 React Router 中的 unstable_usePrompt Hook的詳細用法及案例

React Router 中的 unstable_usePrompt 是一個用于在用戶嘗試離開當前頁面時觸發確認提示的自定義鉤子,常用于防止用戶誤操作導致數據丟失(例如未保存的表單)。 一、unstable_usePrompt用途 防止意外離開頁面:當用戶在當前頁面有…

OSI 7層模型

OSI 7層模型: 1、物理層(光纖等把電腦連接起來的物理手段) 2、數據鏈路層(以太網,確認0和1電信號的分組方式,負責MAC地址,MAC地址用于在網絡中唯一標示一個網卡,相當于網卡的身份證…

視頻編解碼學習十一之視頻原始數據

一、視頻未編碼前的原始數據是怎樣的? 視頻在未編碼前的原始數據被稱為 原始視頻數據(Raw Video Data),主要是按照幀(Frame)來組織的圖像序列。每一幀本質上就是一張圖片,通常采用某種顏色格式…

Redis學習打卡-Day1-SpringDataRedis、有狀態無狀態

Redis的Java客戶端 Jedis 以 Redis 命令作為方法名稱,學習成本低,簡單實用。Jedis 是線程不安全的,并且頻繁的創建和銷毀連接會有性能損耗,因此推薦使用 Jedis 連接池代替Jedis的直連方式。 lettuce Lettuce是基于Netty實現的&am…

告別靜態配置!Spring Boo動態線程池實戰指南:Nacos+Prometheus全鏈路監控

一、引言 1.1 動態線程池的必要性 傳統線程池的參數(如核心線程數、隊列容量)通常通過配置文件靜態定義,無法根據業務負載動態調整。例如,在電商大促場景中,流量可能瞬間激增,靜態線程池容易因配置不合理導…

Flask如何讀取配置信息

目錄 一、使用 app.config 讀取配置 二、設置配置的幾種方式 1. 直接設置 2. 從 Python 文件加載 3. 從環境變量加載 4. 從字典加載 5. 從 .env 文件加載(推薦開發環境用) 三、讀取配置值 四、最佳實踐建議 在 Flask 中讀取配置信息有幾種常見方…

【React中useCallback鉤子詳解】

useCallback 是 React 中的一個性能優化 Hook,用于緩存函數引用,避免在組件重新渲染時重復創建相同的函數,從而減少不必要的子組件渲染或副作用執行。以下是其核心要點: 1. 核心作用 函數記憶化:返回一個記憶化的回調函數,僅在依賴項變化時重新創建函數,否則復用之前的函…

【!!!!終極 Java 中間件實戰課:從 0 到 1 構建億級流量電商系統全鏈路解決方案!!!!保姆級教程---超細】

終極 Java 中間件實戰課:電商系統架構實戰教程 電商系統架構實戰教程1. 系統架構設計1.1 系統模塊劃分1.2 技術選型2. 環境搭建2.1 開發環境準備2.2 基礎設施部署3. 用戶服務開發3.1 創建Maven項目3.2 創建用戶服務模塊3.3 配置文件3.4 實體類與數據庫設計3.5 DAO層實現3.6 Se…