Review
- 2023-02-19 08:00
- 2025-08-13
一、Introduction #
想要优化性能,需要先进行性能测试。
压力测试工具
- ab
- webbench
找到性能瓶颈所在
- top:CPU、内存
- iostat:硬盘
CPU/内存/硬盘/网卡/后端
二、调优方向 #
- 代码优化
- 多进程优化
- 架构优化
- Addon优化
1. 多进程优化 #
Node.js子进程与线程,使用子进程或线程利用更多CPU资源。
线程 #
- 进行运算调度的单元,一个进程可以有很多线程
- 进程内的线程共享进程内的资源
Node.js的事件循环 #
- 主线程运行V8与JavaScript
- 多个子线程通过事件循环被调度
子进程 #
// index.js
const cp = require('child_process');
const childProcess = cp.fork(__dirname + '/child.js');
childProcess.send('hello');
childProcess.on('message', (msg) => {
console.lgo(msg);
})// child.js
process.on('message', (msg) => {
console.log(msg);
process.send(msg + 'world');
});线程 #
const worker = require('worker_threads');Cluster #
2. 进程守护与管理 #
错误监听
process.on('uncaughtException', (err) => {
console.error(err); // 可以上报错误
process.exit(1); // 然后退出进程
})cluster监听
if (cluster.isMaster) {
cluster.on('exit', () => {
// 上报错误
// 并新建一个子进程
setTimeout(() => {
cluster.fork();
}, 5000);
})
}内存监听
if (process.memoryUsage().rss > 1000000000) {
// 上报
console.log('oom');
process.exit(1);
}心跳检测
3. 架构优化 #
动静分离
- 静态内容
- 基本不会变动,也不会因为请求参数不同而变化
- -> CDN分发,HTTP缓存等
- 动态内容
- 各种因为请求参数不同而变动,且变种的数量几乎不可枚举
- -> 用大量的源站机器承载:
- 结合反向代理进行负载均衡 proxy_pass
- 结合反向代理进行缓存 proxy_cache
- Node服务,接入Redis服务,做内存缓存
redis
const Koa = require('koa');
const redis = require('redis');
const app = new Koa();
const cacheRedis = redis('cache');
const backupRedis = redis('backup');
app.use(async(ctx, next) => {
const result = await cacheRedis(ctx.url);
if (result) {
ctx.body = result;
return;
}
// 等待真实请求
await next();
if(ctx.status === 200) {
cacheRedis.set(ctx.url, ctx.body, { expire: 200 })
backupRedis.set(ctx.url, ctx.body, { expire: 200 })
}
// 如果真实请求也失败了,可以返回备份的数据
if (ctx.status !== 200) {
const result = await backupRedis(ctx.url);
ctx.status = 200;
ctx.body = result;
}
});4. Addon #
Node.js Addon 是一种使用 C/C++ 编写的动态链接库,可以直接被 Node.js 调用。它允许开发者通过原生代码扩展 Node.js 的功能,在需要高性能计算或与系统底层交互时特别有用。
Node.js Addon 本质上是遵循特定规范的共享库(在 Windows 上是 .node 文件,在 Unix-like 系统上是 .so 文件),它使用 Node.js 提供的 API 与 JavaScript 交互。
为什么使用 Node.js Addon
- 性能优化:对于计算密集型任务,C/C++ 通常比 JavaScript 更快
- 系统级访问:直接调用操作系统 API 或硬件功能
- 重用现有库:集成已有的 C/C++ 库到 Node.js 生态
- 多线程处理:突破 Node.js 单线程模型的限制
主要开发方式 #
- Node-API (N-API)
- node-gyp
- Rust Neon
1. 原生 Node-API (推荐) #
Node-API(以前称为 N-API)是 Node.js 提供的与 JavaScript 引擎无关的 API,确保 Addon 在不同 Node.js 版本间的兼容性。
特点:
- 稳定的 ABI(应用二进制接口)
- 不依赖特定 V8 版本
- 支持多线程安全
示例
#include <node_api.h>
napi_value Method(napi_env env, napi_callback_info info) {
napi_value greeting;
napi_create_string_utf8(env, "Hello from C++!", NAPI_AUTO_LENGTH, &greeting);
return greeting;
}
napi_value Init(napi_env env, napi_value exports) {
napi_status status;
napi_value fn;
status = napi_create_function(env, NULL, 0, Method, NULL, &fn);
if (status != napi_ok) return NULL;
status = napi_set_named_property(env, exports, "hello", fn);
if (status != napi_ok) return NULL;
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)2. Nan (Native Abstractions for Node.js) #
Nan 是一个头文件库,简化了与不同 Node.js 版本 V8 API 的交互。
特点:
- 比原生 V8 API 更友好
- 处理不同 Node.js 版本的兼容性问题
- 正在被 Node-API 逐渐取代
3. 直接使用 V8 API #
直接使用 V8 引擎提供的 API(不推荐,因为不同 Node.js 版本 V8 API 变化较大)
开发工具链 #
- node-gyp
- binding.gyp
- Rust Neon
- WebAssembly:对于纯计算任务,可以考虑使用 WASM
- Rust 或 Go 编写的 Addon:通过工具链编译为 Node.js Addon
node-gpy #
https://github.com/nodejs/node-gyp Node.js native addon build tool node-gyp:Node.js 的跨平台命令行工具,用于编译原生 Addon
- 基于 GYP (Generate Your Projects) 构建系统
- 需要 Python 和 C++ 编译器
binding.gyp #
配置文件,描述如何构建 Addon
{
"targets": [
{
"target_name": "addon",
"sources": [ "addon.cc" ],
"include_dirs": ["<!@(node -p \"require('node-addon-api').include\")"],
"dependencies": ["<!(node -p \"require('node-addon-api').gyp\")"],
"defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS" ]
}
]
}Neon #
Rust bindings for writing safe and fast native Node.js modules. https://github.com/neon-bindings/neon
Neon 是一个简化 Node.js Addon 开发的 Rust 绑定框架:
- Rust 的安全性:内存安全保证,避免常见的内存错误
- 简化开发:提供高级API隐藏V8细节
- 性能优异:Rust编译的代码性能与C++相当
- 社区活跃:持续维护和更新
缺点:需要学习Rust,对V8 API的抽象可能导致某些高级功能受限
WebAssembly #
WebAssembly 是一种新兴的跨平台技术,特别适合替代计算密集型的 Node.js Addon:
- 无需原生编译:直接运行二进制格式,避免平台兼容性问题
- 安全沙箱:运行在隔离的内存空间中,不会导致进程崩溃
- 跨语言支持:可用 C/C++/Rust/Go 等多种语言编写
- 性能接近原生:执行效率通常能达到原生代码的70-80%
NAPI-RS #
https://github.com/napi-rs/napi-rs 专为Node-API设计的Rust绑定框架:
- 类型安全:自动生成TypeScript类型定义
- 跨版本兼容:基于Node-API,不依赖特定V8版本
- 工程化支持:内置跨平台构建工具链
- 性能优化:特别优化的FFI调用路径
相比Neon,NAPI-RS更适合大型工程和TypeScript项目
Puerts by Tencent #
https://github.com/Tencent/puerts 创新的跨语言绑定解决方案:
- 语言无关设计:同一Addon可在Node.js、Lua等多种环境中使用
- 自动TS类型生成:从C++代码生成TypeScript定义文件
- 声明式API:简洁的DSL描述绑定关系
- 卓越性能:比传统方案快4-5倍,支持V8快速调用优化
独特优势:特别适合需要跨多个脚本引擎共享代码的场景
V8PP #
https://github.com/pmed/v8pp Bind C++ functions and classes into V8 JavaScript engine
[!summary]
- 纯计算任务:优先考虑WebAssembly
- 需要原生扩展:
- Rust技术栈 → NAPI-RS 或 Neon
- 跨语言需求 → Puerts
- 全新项目:可评估Bun/Deno替代Node.js
- 企业级应用:Node-API或NAPI-RS保证长期兼容性