Skip to content

知识点

事件循环

Node.js 的事件循环是单线程的,负责调度和处理异步操作。

执行阶段:

  • Timers (计时器阶段):执行 setTimeoutsetInterval 的回调
  • Pending Callbacks (待处理回调阶段):处理某些系统操作的回调,例如文件系统操作完成后的回调
  • Idle, Prepare (空闲阶段):内部使用
  • Poll (轮询阶段):获取新的 I/O 事件,执行与 I/O 相关的回调
  • Check (检查阶段):执行 setImmediate 回调
  • Close Callbacks (关闭回调阶段):处理如 socket.on("close") 等回调

微任务队列: Promise 的回调会被加入到微任务队列(Microtask Queue),并且优于宏任务队列(如 setTimeout)。


性能优化

1. 异步编程 使用 Promiseasync/awaitstream 来防止阻塞事件循环

2. 代码分解 避免代码逻辑过于复杂,通过模块化拆分大型代码块

3. 负载均衡 使用 cluster 模块或负载均衡工具(如 Nginx)来分发请求到多个 worker

4. 缓存机制 使用缓存(如 Redis、Memcached)来降低数据库查询频率

5. 避免内存泄漏 定期监控和调试内存泄漏,使用 process.memoryUsage() 和工具如 heapdump

6. 使用流 处理大文件时,利用流(Streams)逐块读取或写入,替代一次性加载

7. 性能监控工具 使用 clinic.jsnode --prof 或其他 APM 工具(如 New Relic)分析瓶颈


流(Streams)

流是在 Node.js 中处理连续数据流的抽象,适用于处理无法一次性装载到内存中的大数据。

流的类型:

  1. 可读流 (Readable):从数据源读取数据(如 fs.createReadStream
  2. 可写流 (Writable):将数据写入到某个目标(如 fs.createWriteStream
  3. 双工流 (Duplex):既可读又可写(如 net.Socket
  4. 转换流 (Transform):作为数据的处理和修改器(如 zlib.createGzip

用途示例: 在读取大文件时,可以使用流逐块读取,而不是一次性加载,提高内存利用率和程序效率。


异常处理

1. 监听 processuncaughtException 事件:

process.on('uncaughtException', (err) => {
    console.error('Unhandled Exception:', err);
    process.exit(1);
});

2. 监听 processunhandledRejection 事件:

process.on('unhandledRejection', (reason, promise) => {
    console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});

3. 推荐方式: 使用全局错误处理中间件(在 Express 等框架中)或 try-catch 捕获可能的错误。


敏感信息存储

1. 配置文件 (.env) 使用 .env 文件存储敏感信息,借助第三方库如 dotenv 加载环境变量:

require('dotenv').config();
const dbPassword = process.env.DB_PASSWORD;

2. 加密存储 加密敏感信息存储到数据库,使用 crypto 模块或外部库(如 bcrypt、argon2)

3. 环境变量 敏感信息如 API Key 应通过环境变量传递,避免硬编码

4. 限制权限 限制敏感文件的访问权限

5. 使用配置管理工具 如 HashiCorp Vault,或云上的机密管理系统(如 AWS Secrets Manager)


多线程

Node.js 通过 Worker Threads 模块提供了多线程功能,用于处理 CPU 密集型任务:

const { Worker } = require('worker_threads');

const worker = new Worker('./worker.js', { workerData: { num: 10 } });

worker.on('message', (result) => {
    console.log('Result from worker:', result);
});

worker.on('error', (err) => {
    console.error('Worker error:', err);
});

worker.on('exit', (code) => {
    console.log('Worker exited with code:', code);
});

适用场景:

  • 处理计算密集型任务(如图像处理、文件压缩)
  • 避免阻塞主线程

SQL 注入防护

1. 使用参数化查询 使用数据库库(如 mysql2pg)支持的安全 API:

const query = 'SELECT * FROM users WHERE id = ?';
connection.query(query, [userId], (err, results) => {
    if (err) throw err;
    console.log(results);
});

2. ORM 框架 使用 ORM(如 Sequelize、TypeORM)自动验证和构建安全的 SQL 查询

3. 输入验证和转义 验证用户输入是否合法,通过 validator 库检查输入并转义特殊字符


调试方法

1. 内置调试器 使用 node inspect 启动调试模式:

node inspect app.js

使用 Node.js 提供的命令(如 n, c)逐步调试代码

2. 断点调试 使用 debugger 语句:

debugger;

启动程序会自动暂停在断点处

3. 第三方工具

  • 使用 Chrome DevTools 进行调试
  • 安装 VS Code 插件 Debugger for Node.js,设置断点并调试

4. 日志工具 使用日志工具如 winstonpino,记录关键日志用于定位问题


设计模式

1. 单例模式 只允许创建某个类的唯一实例

class Singleton {
    constructor() {
        if (!Singleton.instance) {
            Singleton.instance = this;
        }
        return Singleton.instance;
    }
}
const instance = new Singleton();
Object.freeze(instance);

2. 观察者模式 配合 EventEmitter 使用:

const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.on('event', () => console.log('Event occurred'));
emitter.emit('event');

3. 工厂模式 动态创建对象

class UserFactory {
    createUser(role) {
        switch (role) {
            case 'admin': return new Admin();
            case 'member': return new Member();
        }
    }
}

4. 中间件模式 常见于 Express

app.use((req, res, next) => { console.log('Middleware'); next(); });

长时间运行应用管理

1. 使用 pm2

pm2 start app.js

支持应用重启、日志分析和负载均衡

2. 日志记录 集成日志工具如 winston,监控应用日志

3. 健康检查 通过 HTTP 服务暴露健康检查端点,确保应用正常运行

4. 内存管理 通过 heapdumpprocess.memoryUsage() 检测内存泄漏

5. 监控工具 结合 APM 工具(如 New Relic、Prometheus + Grafana),实时监控性能和资源使用情况


热重载

1. 使用 nodemon

npm install -g nodemon
nodemon app.js

2. 动态重新加载模块

delete require.cache[require.resolve('./module.js')];
const module = require('./module.js');

3. Webpack 或 Babel 等工具结合热加载

const webpack = require('webpack');
const middleware = require('webpack-dev-middleware');

app.use(middleware(webpack(config)));

4. PM2 的 Watch 模式

pm2 start app.js --watch

模块加载机制

1. 路径解析

  • 如果是核心模块(如 fs):直接解析
  • 如果是相对路径(./module.js):解析为具体文件
  • 如果是包名(my-lib):从 node_modules 目录加载

2. 文件定位 Node.js 尝试按以下顺序找到对应的模块:

  • *.js
  • *.json 文件会被解析为 JSON 对象
  • *.node 文件是用 C++ 编写的编译扩展
  • 如果是目录,寻找 package.jsonmain 字段或 index.js

3. 模块缓存 每个模块首次被加载后会缓存到 require.cache 中,重复加载直接返回缓存

4. 模块包装 每个模块(文件)实际被包装成函数执行