几个关于Node.js的网传面试题

在网络上看到的同样是腾讯的、2020届的、硕士学位学生去找实习遇到的面试题,面试题还比较多,估计面的岗位感觉不只是普通的实习这么简单了,有的很基础有的比较深。

今天抽一些关于Node.js的问题出来学习学习。

(面试题目能记得这么多,让我有点怀疑题目文章的真实性,不过学海无涯,多看看多学学)

上题目:

Node内存管理
Node事件循环机制
Node如何进行缓存
Node守护进程原理
Node单线程的优劣势
Node单线程的情况下如何使用多核心的CPU?

内存管理

首先要明确一个背景事实,Node是基于V8构建的,所以内存管理实际上和V8关系很大。

通过查询资料,V8这个东西毕竟是基于浏览器的使用场景开发的,没有考虑那么多,V8的垃圾回收机制是比较激进的,同时内存的使用限制也很严格。

64位系统,V8能用的内存最多1.4GB,而32位系统是0.7GB,这是死限,但是Node基于C++编写的模块在利用内存的时候不会受到这个限制。

V8把内存空间分成了新生代、老生代两个部分,新生代很容易理解,就是各种新生对象存放的地方,它从堆内存的from空间分配内存。一旦新生对象的用量达到了限制(也就是内存区的末尾),垃圾回收机制就会触发。

垃圾回收使用的算法是Scavenge,回收的对象会被放入到另一块空间 —— to空间,而非存活对象直接释放掉。这一部分新生代空间容量不大,而且垃圾回收频繁。想要让对象进入老生代空间,有两个途径:

  • 一次Scavenge回收,对象存活
  • to空间内存占用比例过高,要腾出空间分给新的对象

老生代空间就是从新生代空间里存活下来的对象,垃圾回收的机制是标记清除和标记整理的结合。前者清理垃圾,后者解决内存碎片的问题。

为了利用多核性能,V8后续引入了更多的机制来做垃圾回收避免停顿,比如增量标记,把垃圾回收拆分成N个小步骤,在程序执行过程中交替执行程序逻辑和垃圾回收,直到全过程完成。

调用内存分配情况的代码:

1
process.memoryUsage();

更多详细的内容在这,也算是给我自己日后复习做个标记:
聊聊V8引擎的垃圾回收

事件循环机制

Node下每一个API都是异步的,事件组成一个队列,不断从队列取出事件执行。新的事件被Emitter触发,然后又进入队列,如此循环反复。

除此之外Node里面还有一个执行栈,队列里的东西要等待执行栈。所有的操作都是进入队列的。队列的话是先进先出的。

整个系统是非阻塞的、事件驱动的,事件循环是Node的一大核心,其具体的运作要深入到libuv这个库。

该部分我个人了解不深,附一篇简略理解的文章:
nodejs的事件循环简单理解

比较深的两篇文章:
一次弄懂Event Loop(彻底解决此类面试问题)
浏览器与Node的事件循环(Event Loop)有何区别?

缓存

这应该指的是高并发的环境下怎么缓存数据,业内常见通用方案在Node下也能用,即memcached和redis。

4 进程守护

用现成的:

forever / pm2 / nohup

自己写:

用node的fork创建子进程,然后配合shell实现守护进程。

优劣

优势在于Event Loop这个东西很好地做高并发,如果你想用Python做高并发也绕不过Event Loop,但是在高并发这条路上国内是golang更受欢迎了。

Node本身就是一个异步I/O、事件驱动的框架,它对I/O密集型的应用是友好的。

劣势自然是和优势相对,它对计算密集型的任务不友好,从Electron的性能问题就能看出来。在这单线程如果程序某个点跑崩了那就是总体都跑崩了,所以异常的捕捉处理在Node下很重要。

再者就是对多核的利用和支持了,在多核CPU发展越来越好的今天这个问题其实不小。不过也有比较好的解决方案,只是构建程序的时候麻烦一些。

利用多核

都是关于Node比较深的问题,这里面看了这么多个我一眼能给出答案的就是这一个,其他都是看了一些资料写的。

利用多核很简单,单线程要利用多核自然是走多进程路线,那么Node程序怎么开多进程并且能方便管理呢?

答案就是大名鼎鼎的pm2,用pm2做进程管理,命令如下:

1
pm2 start index.js -i 4

-i参数:以集群模式启动脚本并管理脚本的运行。

有没有其他方法呢?答案是有,用Node的cluster模块,不过这个感觉不怎么常见。cluster的用法有一些类似于Linux里面的系统调用fork(),如下:

1
2
3
4
5
6
7
8
9
10
11
12
const cluster = require('cluster');
const os = require('os');
const cpuNum = os.cpus().length;

if (cluster.isMaster) {
for (let i=0;i<cpuNum;i++){
cluster.fork();
}
} else {
// worker
...
}

至于怎么合理、高效利用多核,以及集群化之后数据方面要怎么处理等,这个不在题目涉及到的范围内了,以后有机会再说。

推荐

《深入浅出Node》这本书还是得看看的,这本书我还没有看过,所以对Node的了解还是比较浅薄,平时只是用到,了解得还不是比较深。

从这一堆题目来看,这本书还是很值得一看的。现在前端对Node用得可并不少,而且前端其实也要开始顾及后端的一部分活,毕竟微服务的概念再向前端延伸,了解Node的这些东西还是有必要的。