合规国际互联网加速 OSASE为企业客户提供高速稳定SD-WAN国际加速解决方案。 广告
>[success] # node 中常用的IO Node.js 是一个基于事件驱动、非阻塞I/O模型的JavaScript运行时,因此在 Node.js 中,I/O 操作是非常重要的一部分。以下是 Node.js 中常用的 I/O 操作: 1. 文件 I/O:Node.js 提供了一组文件系统(fs)API,可以对文件进行读写操作,例如:读取文件、写入文件、创建目录和删除文件等。 2. 网络 I/O:Node.js 提供了一组网络 API,可以创建和管理网络连接,例如:创建 HTTP/HTTPS 服务器、创建 TCP/UDP 客户端等,还可以使用 Socket.IO 等库进行 WebSocket 编程。 3. 控制台 I/O:Node.js 提供了控制台输出 API,例如:console.log()、console.error() 等,可以在控制台输出日志信息。 4. 进程 I/O:Node.js 提供了一组进程 API,可以对进程进行管理,例如:创建子进程、发送信号等。 5. 事件 I/O:Node.js 是基于事件驱动的,因此事件 I/O 是非常重要的一部分。Node.js 提供了一组事件相关的 API,例如:EventEmitter、process.nextTick() 等。 >[success] # 理解node 中 I/O `Node.js`的**I/O模型属于事件驱动和异步I/O模型**。这种设计使得 Node.js 可以通过事件驱动和异步 I/O 处理**大量并发连接或文件访问**。因此**非阻塞 I/O 的特性,使得它在处理高并发场景下具有很好的性能**。 `Node.js` 提供了多种 `API `来支持各种 I/O 操作,**包括文件系统操作,网络操作**等。由于 `Node.js` [图片来自](https://juejin.cn/book/7171733571638738952/section/7172100279058300935) ![](https://img.kancloud.cn/1a/b1/1ab1c2a291a4b8147d5370f4d8856a0b_736x444.png) >[danger] ##### node 中异步I/O模型 在 `Node.js` 中,`I/O` 操作的实现方式是**异步非阻塞的模型**,即当发起一个` I/O `请求时,`Node.js` 并不会阻塞主线程的执行,而是将其放到一个专门的 `I/O` 线程中执行,**然后通过事件循环机制等待 I/O 线程完成操作,将结果返回给主线程进行后续处** ***** 不是说`js` 是单线程么,怎么出现了一个 `I/O` 线程?**node单线程是对我们观察而言。对底层可不是,只不过其他线程对你不开放的**,如果node真的只是单纯的单线程那么就变成**同步阻塞模型**,这种模型下的 I/O 操作会阻塞主线程的执行,直到 I/O 操作完成并返回结果后才能继续执行后面的代码。这种模型在高并发的情况下效率低下,容易导致系统瓶颈和响应延迟。 >[info] ## node 的事件循环机制 我们知道 JS 是单线程的(node并不是),如果线程池处理完一个任务之后,直接执行上层回调,上层代码就完全乱了。这时候就需要一个**异步通知的机制**,也就是说当一个线程处理完任务的时候,它不是直接去执行上程回调的,而是**通过异步机制去通知主线程来执行这个回调** ***** 在node 的I/O线程执行完的数据是怎么回到主线程。这个过程,I/O操作则委托给`libuv`库处理,`libuv`会将I/O操作分发到系统线程池中的线程中进行处理,当`I/O`操作完成时,线程会将结果通知到`libuv`,`libuv`再将结果通知到`JavaScript`线程,`JavaScript`线程再执行回调函数处理结果。**这个过程有点像发布订阅,将耗时的 I/O 操作交给其他线程或者线程池来完成,然后在主线程中继续执行其他任务,等到 I/O 操作完成后再通过回调函数或者事件来通知主线程获取返回结果**例如,如果是文件读取即读取的内容已经在I/O 线程执行获取完了 >[danger] ##### libuv `libuv `是 Node.js 的一个重要的跨平台库,主要用于处理 I/O 操作、文件系统访问、网络操作、定时器等异步操作。它的作用在于提供一个事件循环机制,用于管理 Node.js 的异步操作,并支持在多个平台上的异步事件处理。libuv 采用非阻塞 I/O 的方式,可以支持 Node.js 处理大量并发连接或文件访问,从而保证了 Node.js 在处理高并发场景下的性能和稳定性。Node.js 中受到 libuv 的管理的事件,基本都是通过回调函数的方式来处理的。同时,libuv 还提供了线程池机制,**可以用于处理 CPU 密集型的任务,以减轻主线程的压力,从而避免造成阻塞。** * 我们编写的JavaScript代码会经过V8引擎,再通过Node.js的Bindings,将任务放到Libuv的事件循环中;libuv(Unicorn Velociraptor—独角伶盗龙)是使用C语言编写的库;libuv提供了**事件循环、文件系统读写、网络IO、线程池**等等内容;在对应的线程位置(workThreads)处理对应的模块,处理完后将结果在(executecallback)在回到对应的事件队列中 ![](https://img.kancloud.cn/09/a5/09a56b1eead57c1349d3c59b19ec950c_949x328.png) >[danger] ##### 浏览器中的事件循环机制 在浏览器中,JavaScript 代码是运行在主线程中的,如果执行一个耗时的 I/O 操作,比如发送 AJAX 请求,就会阻塞主线程,导致用户界面无响应或卡顿。 为了避免这个问题,浏览器的网络模块通常是运行在独立的线程中的,这个线程叫做网络线程(network thread)。当 JavaScript 代码调用异步 API 发起网络请求时,浏览器会将这个请求交给网络线程去处理,然后立即返回到 JavaScript 代码中执行下一行代码。网络线程会在后台执行这个 I/O 操作,等到 I/O 操作完成后,就会触发一个事件,告诉 JavaScript 代码请求已经完成,可以获取返回结果了。 由于 JavaScript 代码是运行在主线程中的,因此网络请求的结果不能直接返回给 JavaScript 代码,而是需要通过事件回调的方式来完成。当网络线程完成请求后,它会将请求结果传递给浏览器的主线程(也就是 JavaScript 线程),然后触发相应的事件,告诉 JavaScript 代码请求已经完成,可以获取返回结果了。这个过程是通过事件驱动来实现的。 >[danger] ##### 二者总结 [浏览器与Node的事件循环(Event Loop)有何区别? | Fundebug博客 - 一行代码搞定BUG监控 - 网站错误监控|JS错误监控|资源加载错误|网络请求错误|小程序错误监控|Java异常监控|监控报警|Source Map|用户行为|可视化重现](https://blog.fundebug.com/2019/01/15/diffrences-of-browser-and-node-in-event-loop/) **Node.js 是通过 libuv 库来实现异步 I/O 操作和事件循环机制的,而浏览器是通过 Web API 实现异步操作**,同时也采用了事件循环机制来保证异步操作的高效执行。无论是在 Node.js 还是在浏览器中,事件循环机制都是实现异步编程的关键,通过将异步操作的回调函数注册到事件循环中,可以保证异步操作的非阻塞和高效执行。 >[danger] ##### 深入案例 当需要从数据库中查询数据并返回给客户端时,可以使用同步和异步两种方式来实现: 同步方式: ~~~javascript javascriptCopy codeconst http = require('http'); const mysql = require('mysql'); const connection = mysql.createConnection({ host: 'localhost', user: 'root', password: 'password', database: 'test' }); const server = http.createServer((req, res) => { const result = connection.query('SELECT * FROM users'); // 同步查询数据库 res.end(JSON.stringify(result)); // 返回结果给客户端 }); server.listen(3000, () => { console.log('Server is running at http://localhost:3000'); }); ~~~ 在上面的代码中,使用了同步方式查询数据库,这会阻塞程序的执行,直到查询完成并返回结果。在查询期间,程序无法处理其他的请求,因此会导致程序的性能下降。 异步方式: ~~~javascript javascriptCopy codeconst http = require('http'); const mysql = require('mysql'); const connection = mysql.createConnection({ host: 'localhost', user: 'root', password: 'password', database: 'test' }); const server = http.createServer((req, res) => { connection.query('SELECT * FROM users', (err, result) => { // 异步查询数据库 if (err) { return console.error(err); } res.end(JSON.stringify(result)); // 返回结果给客户端 }); }); server.listen(3000, () => { console.log('Server is running at http://localhost:3000'); }); ~~~ 在上面的代码中,使用了异步方式查询数据库,这不会阻塞程序的执行,而是将查询操作放到一个队列中,然后立即返回结果给客户端。当查询完成后,会调用回调函数将查询结果返回给客户端。这种方式可以充分利用系统的资源,提高程序的性能和并发处理能力。 ??????????? https://juejin.cn/post/7002106372200333319#heading-0