Node.js 的理解?优缺点?应用场景?
Node.js 是一个开源与跨平台的 Javascript 运行时环境。在浏览器外运行 V8 Javascript,利用事件驱动、非阻塞和异步输入输出模型等基础提高性能
可以理解为 Node.js 就是一个服务端的、非阻塞式I/O(输入/输出)、事件驱动的 Javascript 运行环境
优点:
- 处理高并发场景性能更佳
- 适用 I/O 密集型应用,指的是应用在运行极限时,CPU 占用率仍然比较低,大部分时间再做 I/O 硬盘内存读写操作
缺点:
- 不适用 CPU 密集型应用
- 只支持单核 CPU、不能充分利用 CPU
- 可靠性低,一旦代码某个环境崩溃,整个系统都崩溃
应用场景:
- 善于 I/O ,不善于计算,
- 大量并发的 I/O ,应用程序内部并不需要进行非常复杂的处理
- 与 WebSocket 配合,开发长连接的实时交互应用程序
具体场景:
- 第一大类:用户表单收集系统、后台管理系统、实时交互系统、考试系统、联网软件、高并发量的 web 应用程序
- 第二大类:基于 web、canvas 等多人联网游戏
- 第三大类:基于 web 的多人实时聊天客户端、聊天器、图文直播
- 第四大类:单页面浏览器应用程序
- 第五大类:操作数据库、为前端和移动端提供基于 json 的API
Node.js 有哪些全局对象?
真正的全局对象:
- Class:Buffer:可以处理二进制以及 Unicode 编码的数据
- process : 进程对象。提供有关当前过程的信息和控制
- console
- clearInterval、setInterval
- clearTimeout、setTimeout
- global
模块级别的全局对象:
- __dirname
- __filename
- exports
- module
- require
Node 中的 process 的理解
process 对象常见的属性:
- process.env: 环境变量,例如通过 process.env.NODE_ENV 获取不同环境项目配置信息
- process.nextTick: 与 EventLoop 有关,会在这一次的 event loop 的 call stack 清空后(下一次event loop开始前)再调用 callback
- process.pid: 获取当前进程 id
- process.ppid: 获取当前进程对应的父进程
- process.cwd(): 获取当前进程工作目录
- process.platform: 获取当前进程运行的操作系统平台
- process.uptime(): 当前进程已运行时间
- 进程事件:process.on()
- 三个标准流:process.stdout 标准输出、process.stdin 标准输入、process.stderr 标出错误输出
- process.title 指定进程名称,有的时候需要给进程指定一个名称
Node 中的 fs 模块的理解?
fs (filesystem), 该模块提供本地文件的读写能力,基本上是 POSIX 文件操作命令的简单包装,所有与文件的操作都是通过 fs 核心模块实现
方法:
- 文件读取 :fs.readFileSync
- 文件写入:fs.writeFileSync
- 文件追加写入:fs.appendFileSync
- 文件拷贝:fs.copyFileSync
Node 中的 Buffer 理解?
Buffer(缓冲区) 就是在内存中开辟一片区域(初次初始化为 8kb),用来存放二进制数据
使用方法:
Buffer.from()
Buffer.alloc()
应用场景:
- I/O 操作
- 加密解密
- zlib.js 利用缓存区(Buffer)的功能操作二进制数据流,提供了压缩或者解压功能
Node 中的 Stream 的理解?
流(Stream),是一个数据传输手段,是端到端信息交换的一种方式,而且是有顺序的,是逐块读取数据、处理内容、用于顺序读取 I/O
source---> pipe ----> dest
种类:
- 可写流
- 可读流
- 双工流
- 转换流:可以在数据写入或者读取是修改或转换数据的流
说说 Node 中的 EventEmitter?
EventEmitter 就是 Node 实现事件驱动的基础,这个类实现了 Node 异步事件驱动架构的基本模式---观察者模式。
在这种模式中,被观察者(主体)维护着一组其他对象派来(注册)的观察者,有新的对象对主体感兴趣就注册观察者,不感兴趣就取消订阅,主体有更新的话就一次通知观察者们。
说说对 Node 中的事件循环机制的理解?
事件循环是基于 libuv 实现,libuv 是一个多平台的专注于异步 IO 的库
流程:
- 定时器检测阶段(timers): 本阶段执行 timer 的回调,即 setTimeout、setInterval 里面的回调函数
- I/O 事件回调阶段(I/O callback):执行延迟到下一个循环迭代的 I/O 回调,即上一轮新欢中未执行的一些 I/O 回调
- 闲置阶段:仅系统内部使用
- 轮询阶段:检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 setImmediate() 调度之外),其余情况 node 将在适当的时候在此阻塞
- 检查阶段(check):setImmediate() 回调函数在这里执行
- 关闭事件回调函数( close callback ):一些关闭的回调函数,如:socket.on(‘close’, ....)
宏任务对应有:
- timer queue:setTimeout、setInterval
- poll queue: IO 事件
- check queue:setImmediate
- close queue:close 事件
微任务对应有:
- next tick queue:process.nextTick
- other queue::Promise 的 then 回调、queueMirotask
Node 文件查找的优先级以及 Require 方法的文件查找策略?
- 缓存的模块优先级最高
- 如果是内置模块,则直接返回,优先级仅次于缓存的模块
- 如果是绝对路径 / 开头,则从根目录找
- 如果是绝对路径 ./ 开头,则从当前 require 文件相对位置找
- 如果文件没有携带后缀,先从 js、json、 node 按顺序查找
- 如果是目录,则根据package.json的 mian 属性决定目录的入口文件,默认情况为 index.js
- 如果文件为第三方模块,则会引入 node_modules 文件,如果不在当前仓库文件中,则自动从上级递归查找,直到根目录
对中间件概念的理解,如果封装 node 中间件?
中间件本质是一个回调函数,参数包含请求对象、响应对象和执行下一个中间件的函数
如何实现 jwt 鉴权机制?
JWT( JSON Web Token ),本质就是一个字符串书写规范
Token:分成了三部分,头部(header)、荷载(Payload)、签名(signature)
如何实现:
- 生成 token:登录成功的时候,颁发 token
- 验证 token : 访问某些资源或者接口时,验证 token
优点:
- json 具有通用性,所以可以跨语言
- 组成简单,字节占用小,便于传输
- 服务端无需保存会话信息,很容易进行水平扩展
- 一处生成,多处使用,可以在分布式系统中,解决单点登录问题
- 可防护 CSRF 攻击
缺点:
- payload (荷载)部分仅仅是进行简单编码,所以只能用于存储逻辑必需的非敏感信息
- 需要保护好加密密钥,一旦泄露后后货不堪设想
- 为避免 token 被劫持,最好使用 https 协议
实现文件上传?
- 文件的上传:form 表单 + input 标签
- 文件的解析:使用 koa2 中间件解析文件
Node 性能如何进行监控以及优化?
衡量指标:
- CPU :CPU 负载、CPU 使用率
- 内存
- I/O
- 网络
监控性能: Easy-Monitor 2.0 。在默认模式下,只需要在项目入口文件 require 一次,无需改动任何业务代码即可开启内核级别的性能监控分析
const easyMonitor = require('easy-monitor')
easyMonitor('你的项目名称')
如何优化:
- 使用最新版本的 Node.js
- 正确使用流 Stream
- 代码层面优化:合并查询,将多次查询合并成一次,减少数据库的查询次数
- 内存管理优化:使用池,将频用、可复用对象储存起来,减少创建和销毁操作