zl程序教程

您现在的位置是:首页 >  其他

当前栏目

常见面试题集合之--node

2023-09-27 14:27:10 时间

node模块

请介绍一下require的模块加载机制

1、先计算模块路径
2、如果模块在缓存里面,取出缓存
3、加载模块
4、输出模块的exports属性即可

Node基础

node有哪些特征,与其他服务器端对比

特征:单线程、事件驱动、非阻塞I/O

node 无法直接渲染静态页面,提供静态服务
node 没有根目录的概念
node 必须通过路由程序指定文件才能渲染文件
node 比其他服务端性能更好,速度更快

如何判断当前脚本运行在浏览器还是node环境中

通过判断 Global 对象是否为 window ,如果不为window ,当前脚本没有运行在浏览器中

node怎么跟MongoDB建立连接

  1. 引入mongoose
  2. 使用mongoose.connect()方法连接到MongoDB数据库
  3. 监听连接是否成功
  4. 然后通过node,书写接口,对数据库进行增删改查

V8的垃圾回收机制

如何查看V8的内存使用情况

使用 process.memoryUsage() ,返回如下

{
	rss: 4935680,
	heapTotal: 1826816,
	heapUsed: 650472,
	external: 49879
}

V8的内存分代和回收算法

在V8中,主要将内存分为新生代和老生代两代。新生代中的对象存活时间较短的对象,老生代中的对象存活时间较长,或常驻内存的对象。

哪些情况会造成V8无法立即回收内存

闭包和全局变量

webSocket

webSocket与传统的http有什么优势

  1. 客户端与服务器只需要一个TCP连接,比 http 长轮询使用更少的连接
  2. webSocket 服务端可以推送数据到客户端
  3. 更轻量的协议头,减少数据传输量

进程通信

请简述一下node的多进程架构

面对node单线程对多核CPU使用不足的情况,Node提供了child_process模块,来实现进程的复制,node的多进程架构是主从模式,如下所示:
在这里插入图片描述

var fork = require('child_process').fork;
var cpus = require('os').cpus();
for(var i = 0; i < cpus.length; i++){
 fork('./worker.js');
}

在linux中,我们通过ps aux | grep worker.js查看进程

创建子进程的方法有哪些,简单说一下它们的区别

  • spawn(): 启动一个子进程来执行命令
  • exec(): 启动一个子进程来执行命令,与spawn()不同的是其接口不同,它有一个回调函数获知子进程的状况
  • execFlie(): 启动一个子进程来执行可执行文件
  • fork(): 与spawn()类似,不同电在于它创建Node子进程需要执行js文件

spawn()与exec()、execFile()不同的是,后两者创建时可以指定timeout属性设置超时时间,一旦创建的进程超过设定的时间就会被杀死
exec()与execFile()不同的是,exec()适合执行已有命令,execFile()适合执行文件。

你知道spawn在创建子进程的时候,第三个参数有一个stdio选项吗,这个选项的作用是什么,默认的值是什么。

  • 选项用于配置在父进程和子进程之间建立的管道。
  • 默认情况下,子进程的 stdin、 stdout 和 stderr 会被重定向到 ChildProcess 对象上相应的 subprocess.stdin、subprocess.stdout 和 subprocess.stderr 流。
  • 这相当于将 options.stdio 设置为 [‘pipe’, ‘pipe’, ‘pipe’]。

实现一个node子进程被杀死,然后自动重启代码的思路

在创建子进程的时候就让子进程监听exit事件,如果被杀死就重新fork一下

var createWorker = function(){
 var worker = fork(__dirname + 'worker.js')
 worker.on('exit', function(){
  console.log('Worker' + worker.pid + 'exited');
  // 如果退出就创建新的worker
  createWorker()
 })
}

以上基础上,实现限量重启,比如我最多让其在1分钟内重启5次,超过了就报警给运维

  • 思路大概是在创建worker的时候,就判断创建的这个worker是否在1分钟内重启次数超过5次
  • 所以每一次创建worker的时候都要记录这个worker 创建时间,放入一个数组队列里面,每次创建worker都去取队列里前5条记录
  • 如果这5条记录的时间间隔小于1分钟,就说明到了报警的时候了

如何实现进程间的状态共享,或者数据共享

Kafka这类消息队列工具

中间件

如果使用过koa、egg这两个Node框架,请简述其中的中间件原理

  • 洋葱圈模型,就是说中间件执行就像洋葱一样,最早use的中间件,就放在最外层。处理顺序从左到右,左边接收一个request,右边输出返回response
  • 一般的中间件都会执行两次,调用next之前为第一次,调用next时把控制传递给下游的下一个中间件。当下游不再有中间件或者没有执行next函数时,就将依次恢复上游中间件的行为,让上游中间件执行next之后的代码
const Koa = require('koa')
const app = new Koa()
app.use((ctx, next) => {
 console.log(1)
 next()
 console.log(3)
})
app.use((ctx) => {
 console.log(2)
})
app.listen(3001)
// 执行结果是1=>2=>3

koa中间件实现源码

// 注意其中的compose函数,这个函数是实现中间件洋葱模型的关键
// 场景模拟
// 异步 promise 模拟
const delay = async () => {
 return new Promise((resolve, reject) => {
 setTimeout(() => {
  resolve();
 }, 2000);
 });
}
// 中间间模拟
const fn1 = async (ctx, next) => {
 console.log(1);
 await next();
 console.log(2);
}
const fn2 = async (ctx, next) => {
 console.log(3);
 await delay();
 await next();
 console.log(4);
}
const fn3 = async (ctx, next) => {
 console.log(5);
}
 
const middlewares = [fn1, fn2, fn3];
 
// compose 实现洋葱模型
const compose = (middlewares, ctx) => {
 const dispatch = (i) => {
 let fn = middlewares[i];
 if(!fn){ return Promise.resolve() }
 return Promise.resolve(fn(ctx, () => {
  return dispatch(i+1);
 }));
 }
 return dispatch(0);
}
 
compose(middlewares, 1);