know

node相关

高优先级知识点,需要熟练掌握。

# 1. node 的优势

  1. 高并发,不会消耗很多的 CPU,如果 CPU 占用率高,则表明应用存在很多同步操作,导致异步任务回调被阻塞
  2. 非阻塞异步 io
  3. 强大的 NPM 包管理工具

# 2. Node 的性能优化的方式有

  1. 使用最新版本 Node.js
  2. 正确使用流 Stream (大文件可以通过流的形式发送,不需要将其完全读入内存)
  3. 代码层面优化
  • 3.1 合并查询,将多次查询合并一次,减少数据库的查询次数
  1. 内存管理优化
  • 4.1 新生代:对象的存活时间较短。新生对象或只经过一次垃圾回收的对象
  • 4.2 老生代:对象存活时间较长。经历过一次或多次垃圾回收的对象

# 3. 中间件概念的理解

中间件主要是指封装 http 请求细节处理的方法

# 4. 如果一个中间件没有调用 await next(),那么后续的中间件将不会执行。

当一个中间件函数执行完成并且没有调用 await next() 时,它不会将控制权交给下一个中间件,而是直接返回或抛出异常。

# 5. Node.js 如何调试

  1. console.log()
  2. --inspect 或者 --inspect-brk 来启动 chrome://inspect
  3. 使用第三方调试工具 vscode 、node-inspector、ndb、WebStorm

# 6. koa 和 express 有哪些不同

基于 ES6 generator 特性的异步流程控制,解决了 "callback hell" 和麻烦的错误处理问题。

# 7. 两个 Node.js 进程如何通信?

9.1 不同一台电脑 使用 HTTP 协议

9.2 同一台电脑

  1. child_process 模块
  2. 自定义管道(net)

# 8. SELECT

SELECT name,country FROM Websites WHERE country='CN' AND alexa > 50;

# 9. INSERT INTO

INSERT INTO table_name (column1,column2...) VALUES (value1,value2,...);

# 10. UPDATE

UPDATE Websites SET alexa='5000', country='USA' WHERE name='菜鸟教程';

# 11. DELETE

DELETE FROM Websites WHERE name='Facebook' AND country='USA';

# 12. MySQL 的表连接类型

  1. 内连接(INNER JOIN) 只返回两个表中满足连接条件的记录。

    SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.id;
    
  2. 左连接(LEFT JOIN 或 LEFT OUTER JOIN) 返回左表的所有记录,以及右表中满足连接条件的记录。右表没有匹配时,结果为 NULL。

    SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id;
    
  3. 右连接(RIGHT JOIN 或 RIGHT OUTER JOIN) 返回右表的所有记录,以及左表中满足连接条件的记录。左表没有匹配时,结果为 NULL。

    SELECT * FROM table1 RIGHT JOIN table2 ON table1.id = table2.id;
    
  4. 全连接(FULL JOIN 或 FULL OUTER JOIN) 返回两个表中所有记录,只要其中一个表有匹配就显示。MySQL 不直接支持 FULL JOIN,可以用 UNION 实现。

    SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id
    UNION
    SELECT * FROM table1 RIGHT JOIN table2 ON table1.id = table2.id;
    
  5. 交叉连接(CROSS JOIN) 返回两个表的笛卡尔积,即每个表的每一行都与另一个表的每一行组合。

    SELECT * FROM table1 CROSS JOIN table2;
    
  6. 自连接(SELF JOIN) 表与自身进行连接,常用于树形结构或需要比较同一表中不同记录的场景。

    SELECT a.id, a.name, b.name AS parent_name
    FROM category a
    LEFT JOIN category b ON a.parent_id = b.id;
    
  7. 自然连接(NATURAL JOIN) 自连接用于表自身的层级或关联查询,自然连接适合字段名一致且需要自动匹配的场景。

    SELECT * FROM table1 NATURAL JOIN table2;
    

# 13. Koa 源码解析

网址 (opens new window)

  1. application.js 是 koa 的入口文件
  2. 上下文 ctx,其实就一个简单的对象暴露,里面的重点在 delegate,这个就是代理 3 和 4. request.js、response.js ,去取 headers 或者设置 headers、还有设置 body 等

# 14. Koa 中间件有哪些

  1. koa-router
  2. koa-bodyparser
  3. koa-static: 提供静态文件服务,例如,CSS、JavaScript 文件等。
  4. koa-logger: 记录请求日志,方便调试和监控。
  5. koa-compress: 压缩响应体,减小传输数据的大小,提高性能。
  6. koa-session: 处理会话,提供用户状态的存储和获取。
  7. koa-cors: 处理跨域请求,允许或禁止跨域资源共享。
  8. koa-jwt: 处理 JSON Web Token(JWT)认证。
  9. koa-views 对进行视图模板渲染,支持 ejs, nunjucks 等模板引擎
  10. koa-onerror:在服务器产生错误(throw 抛出等)后自动重定义到指定路径

# 15. Koa 与 Express 对比

  1. 中间件机制 Express 默认集成了路由、模板等常用功能,中间件采用回调函数;Koa 不集成任何中间件

  2. 异步处理 Express 通过回调(Callback)函数处理异步,容易出现回调地狱;Koa 基于 ES6 的 async/await 或 generator,异步代码更优雅,错误处理更方便。

  3. 响应机制 express 在调用 res.send 方法后就立即响应了,而 koa 则是在所有中间件调用完成之后,在最外层中间件进行响应

  4. 错误处理 express 对错捕获处理起来很不友好,每一个回调都拥有一个新的调用栈,因此你没法对一个 callback 做 try catch 捕获,你需要在 Callback 里做错误捕获,然后一层一层向外传递。

  5. 性能 Koa 更轻量,底层直接基于 Node.js http 模块,性能更高;Express 功能多,体积更大。

# 16. koa 中间件执行顺序——剥洋葱模型实现原理

Koa 可以使用 ES6 中引入的生成器函数(generator functions)来实现洋葱模型。

总结:Koa 的洋葱模型 = compose 递归 Promise 链 ⇒ 请求先由外向内穿透再由内向外回溯,优雅地把「前置逻辑」「核心业务」「后置逻辑」串成一条可插拔的流水线

# 16.1. koa 中间件执行顺序

const Koa = require('koa')
const app = new Koa()

app.use(async (ctx, next) => {
  console.log(1) // 1️⃣ 外层
  await next() // 2️⃣ 进入下一层
  console.log(2) // 5️⃣ 返回外层
})

app.use(async (ctx, next) => {
  console.log(3) // 2️⃣ 中层
  await next()
  console.log(4) // 4️⃣ 返回中层
})

app.use(async (ctx, next) => {
  console.log(5) // 3️⃣ 内层
  console.log(6) // 3️⃣ 内层
  next() // 有没有next()都可以,因为是最后一层
})

/**
 *访问 http://localhost:3000/ 输出结果:
 * 1 3 5 6 4 2
 */
app.listen(3000)

# 16.2. 核心:compose 如何把数组变 Promise 链?

  1. 每调用一次 next() → dispatch(i+1),形成 递归 Promise。
  2. 递归先“压栈”到最深层,再“弹栈”返回,自然形成 环绕执行
function compose(middleware) {
  return function (ctx, next) {
    let index = -1
    return dispatch(0)

    function dispatch(i) {
      if (i <= index) throw new Error('next() called multiple times')
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next // 边界
      if (!fn) return Promise.resolve()
      try {
        return Promise.resolve(fn(ctx, dispatch.bind(null, i + 1)))
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

# 16.3. 框架集成:Koa 的 3 步调用链(可选记)

框架集成:Koa 的 3 步调用链

  1. app.use(fn) 把中间件 push 进数组。
  2. app.listen() 内部生成 const fn = compose(this.middleware)
  3. 每次请求调用 fn(ctx),完成整个洋葱流程

# 17. 说说你对 koa 洋葱模型的理解

它通过中间件(Middleware)机制实现了业务逻辑的分层和复用

Koa 洋葱模型的处理流程可以大致分为四个阶段:

  1. 请求阶段:从外到内依次执行请求相关的中间件,例如解析请求体、设置响应头等操作。

  2. 业务阶段:执行业务逻辑相关的中间件,例如处理授权、验证身份、路由分发等操作。

  3. 响应阶段:从内到外依次执行响应相关的中间件,例如格式化响应数据、设置响应头等操作。

  4. 错误处理阶段:如果在前面的中间件过程中出现了错误,则会跳过后续中间件并交给错误处理中间件来处理异常情况。

# 18. node 有哪些内置模块

  1. fs
  2. http/https
  3. net
  4. path
  5. process
  6. steam
  7. os
  8. url
  9. util
  10. events
  11. querystring
  12. crypto
  13. buff
  14. child_process
  15. timer
  16. zlib

# 19. nestjs 实现的原理

总结:NestJS 的本质 =「装饰器 + 元数据 + IoC 容器」组成的运行时框架,它把 Express/Fastify 的「函数式」API 映射为「类 + 装饰器」的声明式 API,并在启动阶段通过反射扫描元数据,完成依赖注入与路由注册。

1.扫描装饰器 → 2. 生成路由 + 依赖表 → 3. IoC 容器注入 → 4. 挂到 Express/Fastify 于是你写的「类 + 装饰器」就等价于传统的「一堆回调函数」,却拥有了类型安全、依赖注入、生命周期和 AOP 的全部能力。

  • 21.1 IoC(Inversion of Control,对象全由容器自动装配)
  • 21.2 AOP(面向切面)通用逻辑(日志/权限/异常)用装饰器横插即可
  1. 基于 Node.js 和 Express/Koa NestJS 底层使用 Node.js,默认集成 Express,也可切换为 Koa 作为底层 HTTP 框架。

  2. 依赖注入(DI)机制 借鉴 Angular 的依赖注入思想,通过装饰器和容器自动管理服务、控制器等实例的生命周期和依赖关系。

  3. 模块化开发 所有功能都以模块(Module)为单位组织,模块之间可以自由组合和复用,提升项目可维护性和扩展性。

  4. 装饰器语法 使用 TypeScript 的装饰器(@Controller、@Injectable、@Module 等)描述路由、服务、模块等,代码结构清晰。

  5. 中间件与管道机制 支持中间件、守卫(Guard)、拦截器(Interceptor)、管道(Pipe)等,灵活处理请求生命周期和数据校验。

  6. 统一异常处理 提供全局异常过滤器,统一处理错误和异常响应。

  7. 生态丰富 全面支持 TypeScript,类型安全,内置支持 WebSocket、GraphQL、微服务、数据库 ORM(TypeORM、Prisma 等),方便扩展和集成。

# 20. node 程序 CPU 过高的常见原因

  1. 大量同步阻塞操作 比如频繁使用同步的文件或网络操作,导致事件循环被阻塞,无法及时处理其他任务。

  2. 死循环或递归 代码中存在无限循环或递归,导致 CPU 持续高负载。

  3. 大数据量处理未分片或异步化 一次性处理大量数据(如大文件、复杂计算)没有采用流式或异步方式,导致主线程被占满。

  4. 内存泄漏 对象未及时释放,导致垃圾回收频繁或内存占用过高,间接影响 CPU。

  5. 频繁的 I/O 操作未优化 例如频繁读写磁盘、数据库或网络请求,未做批量处理或缓存优化。

  6. 第三方库或依赖性能问题 使用的 npm 包存在性能瓶颈或 bug,导致 CPU 占用异常。

  7. 日志、监控等中间件写入过多 日志量过大或监控频率过高,影响主业务流程。

  8. 进程数量过多或负载均衡不合理 多进程模式下进程数设置不合理,或负载均衡策略不当,导致部分进程 CPU 过高。

排查建议: 可使用 node --inspecttoppm2 monit 等工具进行性能分析,定位具体问题。

# 21. node 程序内存过高的常见原因

  1. 内存泄漏 对象、定时器、闭包等未及时释放,导致内存持续增长。

  2. 缓存未合理清理 使用全局变量或缓存时未定期清理,数据不断堆积。

  3. 大对象或数组频繁创建 频繁创建大对象、数组或 Buffer,未及时释放或复用。

  4. 未关闭的数据库或文件句柄 数据库连接、文件句柄等未正确关闭,导致资源占用。

  5. 第三方库或依赖存在内存问题 使用的 npm 包存在内存泄漏或管理不当。

  6. 高并发请求导致堆积 短时间内大量请求,导致内存瞬时占用过高。

  7. 错误的循环或递归 循环或递归中不断分配新内存,未释放旧数据。

排查建议: 可使用 node --inspectheapdumpprocess.memoryUsage() 等工具进行内存分析,定位具体问题。

# 22. nodejs 的父子进程通讯方式有哪些

  1. spawn、child_process

  2. 父子进程通讯:使用 IPC 模块,父进程发送消息给子进程,子进程接收消息并处理。

// 主进程(main.js)
const { fork } = require('child_process') // fork 是 spawn 的特例,自动创建 IPC 通道

const child = fork('./child.js')

// 主进程发送消息
child.send({ type: 'task', data: '计算 1+2' })

// 主进程接收消息
child.on('message', msg => {
  console.log(`子进程返回:${msg.result}`) // 输出:子进程返回:3
})

// 子进程(child.js)
process.on('message', msg => {
  if (msg.type === 'task') {
    const result = 1 + 2
    // 子进程发送消息给主进程
    process.send({ result })
    process.exit() // 完成后退出
  }
})
  1. 共享内存(SharedArrayBuffer)
// 主进程(需用 --experimental-sharedarraybuffer 启动)
const { fork } = require('child_process')
const buffer = new SharedArrayBuffer(4) // 4 字节共享内存(存储一个 32 位整数)
const arr = new Int32Array(buffer)
arr[0] = 100 // 主进程写入数据

const child = fork('./child.js')
child.send(buffer) // 发送共享内存引用
// 子进程(child.js)
process.on('message', buffer => {
  const arr = new Int32Array(buffer)
  console.log(`共享内存数据:${arr[0]}`) // 输出:100
  arr[0] = 200 // 修改共享数据
})

// 主进程后续可读取修改后的数据
setTimeout(() => {
  console.log(`主进程读取共享数据:${arr[0]}`) // 输出:200
}, 100)
  1. 文件 / 数据库
// 主进程写入数据到文件
const fs = require('fs')
fs.writeFileSync('data.txt', '需要处理的数据')
// 启动子进程处理文件
const { exec } = require('child_process')
exec('node child.js', (err, stdout) => {
  console.log(`子进程处理结果:${stdout}`)
})

// 子进程(child.js)读取并处理文件
const data = fs.readFileSync('data.txt', 'utf8')
const result = data.toUpperCase() // 示例:转为大写
console.log(result)
  1. 网络通信(TCP/UDP/Socket)
  2. 进程内:模块间通过 EventEmitter 实例的 on/emit 交互。
  3. 进程间:结合 child_process.fork 的 IPC 通道,通过 process.on('message') 和 process.send() 间接使用 emit 机制。
  4. 使用自定义管道
// server.js
const net = require('net')
const fs = require('fs')

const pipeFile =
  process.platform === 'win32' ? '\\\\.\\pipe\\mypip' : '/tmp/unix.sock'

const server = net.createServer(connection => {
  console.log('socket connected.')
  connection.on('close', () => console.log('disconnected.'))
  connection.on('data', data => {
    console.log(`receive: ${data}`)
    connection.write(data)
    console.log(`send: ${data}`)
  })
  connection.on('error', err => console.error(err.message))
})

try {
  fs.unlinkSync(pipeFile)
} catch (error) {}

server.listen(pipeFile)
// client.js
const net = require('net')

const pipeFile =
  process.platform === 'win32' ? '\\\\.\\pipe\\mypip' : '/tmp/unix.sock'

const client = net.connect(pipeFile)
client.on('connect', () => console.log('connected.'))
client.on('data', data => console.log(`receive: ${data}`))
client.on('end', () => console.log('disconnected.'))
client.on('error', err => console.error(err.message))

setInterval(() => {
  const msg = 'hello'
  console.log(`send: ${msg}`)
  client.write(msg)
}, 3000)

# 23. Buffer 是什么?

Buffer 是一个内置类,用于表示二进制数据(如文件内容、网络流、加密数据等)。它是 Node.js 处理 I/O 操作(如文件读写、网络通信)的基础,提供了高效的二进制数据处理能力。 核心特点

# 23.1. 直接操作内存

Buffer 直接分配原始内存(不经过 JavaScript 堆),适合处理大量二进制数据,避免垃圾回收的性能开销。没有引用指向 Buffer 时,内存会被垃圾回收。

# 23.2. 固定长度

创建后大小固定,无法动态调整(但可通过 Buffer.concat() 合并多个 Buffer)。

# 23.3. 高效性能

专为二进制数据设计,提供多种编码转换(如 UTF-8、Base64、Hex 等)和位操作方法。

# 23.4. 全局可用

无需 require() 即可在 Node.js 环境中使用。

# 23.5. Buffer 时长度不同

(如 utf8 中一个汉字占 3 字节,base64 中每 3 字节转换为 4 字符)。

上次更新: