深入浅出nodejs -- 异步IO,异步编程

异步 IO,异步编程

异步 IO

资源分配

一般的话会使用单线程串行一次执行,还有多线程并行完成,但是单线程串行执行,很明显速度会很慢,性能会很差,而多线程的话,可能会早场死锁以及状态的同步问题。

于是 Nodejs 采用的单线程,异步 IO,用单线程来避免死锁以及状态同步的问题,而异步的 IO,则避免了阻塞。而正对无法利用多核 CPU 的问题,Node 提供了子进程的方式来搞笑利用 CPU 和 IO。

异步 IO 与非阻塞 IO

异步和非阻塞其实是两回事。

对于操作系统对内核的 IO 来说,只有阻塞与非阻塞。

阻塞就是就是简单的所有磁盘寻道,读取数据,复制数据之后才返回。

非阻塞 IO 则是系统发出查询的请求后,请求到达之后不带数据返回,然后通过轮询的方式查看什么时候结束了再来拿取数据。

这种轮询也经历了不断地进步,从不断的发送 read;到后来 select 一次可以检查很多次,因为是数组;到后来的 poll,以链表形式突破了长度限制;到后来的 epoll,该方案是 linux 下效率最高的事件唤醒,因为他会休眠到结束,而不会一直轮询。还有 kqueue,不过这个只在 FressBSD 系统下存在。

但是注意在轮询或者休眠的状态下实际上主线程是闲置的,是利用不足的。

Linux 下有那种理想的异步 IO 方式,AIO,但是有缺陷,无法利用系统缓存。

现实中的异步 IO

其实现实中的异步 IO 是多线程的的,让部分线程来通过阻塞的或者非阻塞+轮询的形式来拿到数据,然后让一个线程进行计算处理,就实现了异步 IO。

最终的结果就是 nodejs 通过 libuv 来运行在 windows 和 linux 系统上。

注意我们时常的说 Node 是单线程的,但是其实仅仅只是 js 执行在单线程中而已,node 中,内部完成 IO 任务的其实都是另有线程池的。

非 IO 的异步 API

除了 IO 之外,还有些异步的 API,分别是 setTimeout,setInterval,setImmediate 和 process.nextTick

一般浏览器端我们想要立即异步执行一个函数,就setTimeout(function(){},0)就行了,但是定时器需要动用红黑树,创建定时器对象和迭代,浪费性能。

process.nextTick 则是比较轻量的,直接将回调函数放入队列。

setImmediate 也是类似的,但是这个的优先级比 process.nextTick 要低,并且如果同时写两个的话,执行了一个之后,会直接进行下一轮循环,而不是继续执行。

服务器模型

  • 同步式:一次只能处理一个请求,其余的都得等待。
  • 每进程/每请求:每个请求一个进程,但是扩展性不强,因为系统资源只有那么多。
  • 每线程/每请求:每个请求启动一个线程,尽管线程比较轻量,但是会占内存,导致服务器缓慢。

Node 是通过事件驱动处理请求,不创建额外的对应线程。但是当大量请求来到的时候,究竟 node 发生了什么??tudo??

异步编程

高阶函数

原来高阶函数就是将函数作为参数或者将函数作为返回值的函数。

偏函数

就是说通过指定部分参数来产生一个新的定制函数的函数

难点

  • 异常处理

异步方法通常在第一个阶段提交请求后就立即返回了,因为异常不一定发生在这个阶段,try/catch 没啥用。所以一般编写的异步函数的 callback 的第一个函数作为 err 对象,如果为 null 就代表没有错误,这其实是一种规范。

  • 函数嵌套过深

当操作存在依赖关系时,我们可能会写成 callback hell,当然这个世纪问题也有很多解决办法。

  • 阻塞代码

没办法像 sleep()这样的方法来将线程沉睡一下的,只能规划好代码结构调用 setTimeout 方法来做

  • 多线程编程

node 无法直接利用多核的好处,但是可以使用 child_process 来解决这个问题,深层次的是 cluster 模块

异步编程解决方案

事件发布/订阅的模式

比如 node 自身提供的 events 模块,就是一个发布/订阅的模式。这个模式常常用来解耦业务,将不变的封装在组件内部,将容易变化,自定义的通过事件暴露给外部调用。

其实这种模式就是一种钩子机制,Node 很多对象都是黑盒的,通过事件钩子,可以比较清晰的看出组件的功能。

比如 HTTP 请求,我们只需要关注 error,data,end 等等事件就可以了。

NODE 对事件发布订阅做了一些额外的处理:

  • 事件超过了 10 个监听器,会得到警报,因为设计者认为会出现内存泄露,调用 emitter.setMaxListeners(0)可以将限制去掉
  • 为了处理异常,EventEmitter 对 error 事件进行特殊对待,如果监听了,就会交给监听器处理,否则作为异常抛出

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 981909093@qq.com

文章标题:深入浅出nodejs -- 异步IO,异步编程

文章字数:1.4k

本文作者:泽鹿

发布时间:2019-08-28, 16:45:23

最后更新:2019-08-28, 16:45:23

原始链接:http://panyifei.github.io/2019/08/28/读书笔记/深入浅出nodejs/异步IO,异步编程/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏