您的位置:首页 > Web前端 > Node.js

深入浅出nodejs读书笔记与联想

2020-11-11 18:34 162 查看

Node

node是单线程的吗?为什么?

node并非单线程,node本身还额外存在IO线程,在写c++扩展的时候会用到。node基于v8,在执行js时,是单线程的。

高并发的本质是什么?

高并发的表现是高IO,本质是提高多核CPU的利用率。再具体的讲也就是:系统内核缓冲区资源的高效利用

并发优化的软件优化方向

  • cpu、内存资源、操作系统的调度优化。多核cpu与其附带内存的调度等。
  • 网卡 如何充分利用多核cpu资源,或者说如何利用多核cpu分摊网卡压力
  • 数据包传输(MTU)的中断对 系统资源的损耗
  • 内核级别的优化:系统进程的最大连接数等
  • 应用级常用数据的合理缓存(进程缓冲区与内核缓冲区的合理调度)

进程间通信的几种方法?

套接字、管道、信号量、消息队列、共享内存

为什么要用node ?

  • 前后端编程语言的统一,能够提高开发信效率。
  • 异步非阻塞IO能够充分利用单核性能,减少多线程模型上下文调度所耗费的资源。
  • 通过事件驱动模型,提高服务端的并发能力。
  • 方便前端根据需要做服务端渲染,按需分会内容等等
  • 方便前端定制数据逻辑,降低前后端的沟通成本。让前后端都更能关注自己领域的问题。

node(commonjs) 模块的加载过程

  • 路径分析 1. 核心模块(已被编译为二进制文件, 加载最快) 2. 带有具体路径的模块(将相对路径解析为绝对路径进行查找) 3. 第三方模块 (可通过module.path查看), 从当前路径下的 node_modules 一层层往上查找一直到根目录,若没有,继续在node全局路径一层层查找。路径较深,查找较慢。
  • 文件定位 1. 优先从缓存中查找 2. 对于没有扩展名的情况,node有限匹配文件。按照 .js .json .node 的顺序依次查找,若没有匹配到文件,则尝试一层层匹配目录,若寻找到目录,则根据目录下的package.json 的main入口文件表示寻找,若没有找到,则尝试指定 index.js index.json , index.node 作为指定文件进行查找。若仍未找到,则报错。
  • 编译执行 三种主要的文件类型可以被成功加载 .js 文件:会被fs模块同步加载后利用v8进行编译执行 .node 文件: c/c++扩展文件,通过dlopen() 方法加载执行 .json :通过fs模块加载后,利用JSON.parse() 进行导出 其他文件:均当做js文件加载

阻塞与非阻塞的区别?

  • 阻塞IO需要等待操作系统级别的所有操作完成后,后续操作才能完成(以读取文件为例,需要经过磁盘寻址、读取数据、复制到内存中所有完成之后,其他操作才能继续执行,这期间CPU资源处于空闲)
  • 阻塞IO在 执行过程中通过直接文件描述符(操作系统内核与应用程序之间的凭证)获取执行结果,无法对文件描述符进行释放;非阻塞IO,则在执行后不直接通过fd获取结果,执行结束后则再次通过fd获取执行结果, fd可以被释放。

阻塞与非阻塞都有什么优势、劣势?

  • 阻塞 优势:在应用程序层面来讲,更有利于编码工作的进行,以及提高debug效率等。 劣势:无法完全利用单核CPU资源实现高并发。
  • 非阻塞
    优势:1. 相对于阻塞能够尽可能多得利用单核CPU的资源 2. 对于GUI层面,能够提高视觉得流畅度,例如浏览器引擎
    劣势:1. 编程层面,如果没有优秀的代码组织能力,会使工程级的项目代码可读性降低,变得难以维护 2. 非阻塞的异步轮询机制也会在一定程度上浪费CPU资源。

node 是如何做到跨平台的 ?

  • 在操作系统的上层添加了层平台兼容层 libuv,平台层统一的产出文件为 .node,通过执行.node文件,能够在不同平台执行c++代码,如类unix为.so,windows为.dll

什么是高可用性?

node的高可用性需要考虑什么问题?

  • 多核CPU的利用
  • 进程异常(父子进程)
  • 内存监控与异常追踪
  • 自动重启
  • 多node进程负载均衡
  • 缓存
  • 单元测试与性能测试

如何尽可能优化node cpu的利用率 ?

  • 端口监听,启动多node进程处理不同任务。
  • 拆分cpu密集型业务为单个子任务
  • cpu密集型业务开启子进程执行
  • 在遇到大内存操作时,使用buffer或stream,避开v8引擎对堆内存的限制。

process.nextTick() 与 setImmediate() 的区别?

process.nextTick() 的回调放在数组中,每轮循环,将数组中的所有回调全部执行。setImmediate() 的回调放在一个链表中,每轮循环时,执行链表的第一个回调函数。 process.nextTick()属于idle观察者,setImmediate属于check观察者。 在一次循环检测中的优先级:idle观察者 > IO观察者 > check观察者

node 观察者模式中的:idle观察者、IO观察者、check观察者区别?

具体区别: 举例:process.nextTick()属于idle观察者,setImmediate属于check观察者。 在一次循环检测中的优先级:idle观察者 > IO观察者 > check观察者

js为何被设计成单线程?

  • 首先就是为了简单快捷,主要被用作浏览器脚本语言、没有复杂的计算工作,大多为交互操作。若设计为多线程,单从上下文的切换来看就已经得不偿失。
  • 其次为了解决单线程同步阻塞问题,js天然支持异步任务,可以极大程度上改善js的执行效率。

描述一下node的执行逻辑?怎样算一次事件循环?

首先js分为解析与执行两个阶段。解析阶段会对每个作用域中的变量进行声明、存储,并最终生成作用域链。 执行阶段,顺序执行逻辑代码,遇到同步任务,则进入调用栈(压栈、执行、出栈),遇到异步任务,则注册至eventTable中,当异步任务满足触发条件时,进入eventQueue, 调用栈中的同步任务执行完成后,会从eventQueue中检测是否有异步任务,如果存在,则每次取出一个异步任务到调用栈中执行,一直如此循环。具体得来讲:一次事件循环(tick)可以看做是 一次宏任务或一次微任务的执行。

  • 验证一次tick:
console.log("start");
process.nextTick(() => {
console.log("nextTick")
})
setTimeout(() => {
console.log("settimeout");
}, 0)
new Promise((resolve) => {
console.log("promise");
resolve()
}).then(() => {
process.nextTick(() => {
console.log("nextTick2")
})
console.log("promise then")
})
console.log("end")

js中异步任务是如何执行的?

js中异步任务的执行可以看做是异步宏任务和微任务的交替执行过程。宏任务包括:整体全局同步script、setTimeout、setInterval、IO、UI交互事件、setImmediate(node);微任务包括:process.nextTick() (node)、promise

浏览器执行机制?

  • 浏览器是多进程的,其中包括:browser核心进程、GUI进程、renderer进程。
  • 每一个选项卡都是一个renderer进程,每一个renderer进程又是多线程的,其中包括:js引擎线程(与GUI线程互斥)、GUI线程、事件处理线程、定时器线程、http请求线程 在一个一面首次加载时,js引擎线程与GUI线程互斥共同处理页面布局,在解析HTML的过程中,首先解析DOM树(二进制、tag、node、tree),遇到js脚本与css脚本,则启动http线程加载相应的资源,此时GUI的渲染是被堵塞的,等到脚本加载完成并执行后,继续解析渲染树:解析css树、最终将dom树与css树形成渲染树。页面最终渲染后,产生的异步事件,包括交互时间、定时器事件等,则交给事件处理线程与定时器线程解决。

v8的垃圾回收机制是怎样的?

v8使用分代垃圾回收机制,根据内存的使用频率,将内存分为新生代内存与老生代内存。

  • 新生代内存:Cheney垃圾回收算法:将新生代内存一分为二,每次回收,检查存活对象并复制进另一半内存中。核心思想为空间换时间,效率较高,但内存会折半。
  • 老生代内存:采用mark-sweep(标记清除)为主与mark-compack(标记整理)为辅的机制

v8的内存限制?原因?

内存限制:64位系统约为1.4G,32位系统约为0.7G。 原因:可使用的内存越大,垃圾回收带来的性能损耗就会越严重,响应的滞后感就会越加明显。

js中应该多用局部变量还是全局变量?为什么?

  • 从执行机制方面看,js变量寻址是按照作用域链由内层作用域向外层层寻找的,故局部变量的时间消耗更低。
  • 从垃圾回收机制来看:js的局部变量使用频率较低,会被js当做新生代内存进行回收,回收效率更高。
  • 从程序维护的便利性看:全局变量更容易被污染。
  • 从代码可读性看:局部变量更有利于程序的阅读。 综上,在能满足开发需要的情况下,优先使用局部变量。

node 中内存泄漏的主要原因:

  • 缓存:尽量使用进程外缓存,尽量不使用v8内存进行缓存,不得已时需考虑:大小限制、过期时间、更新策略
  • 队列消费不及时:队列的生产速度远远大于消费速度。队列应该做最大值限制、或监控或位队列添加有效期。
  • 作用域缓存未释放:闭包的滥用

node的child_process中 spawn 与 exec 的区别:

  • 参数不同, spawn使用数组,exec使用字符串,exec更适合执行复杂命令。
  • 返回数据的大小限制不同,exec限制为200k
  • 返回数据的方式不同:spawn在子进程中一有结果数据就会返回,exec则在子进程最终执行结束后才会返回。

前端项目在做单元测试时需要注意哪些问题?

  • 测试的颗粒度,单元代码的高内聚。
  • 异步测试
  • 超时timeout,与异常逻辑测试。
  • 兼容性测试
  • 接口测试

GPU加速适合什么样的场景?

  • 精度低、量大、高并行计算,如图像、视频等
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: