性能调优

Review

  1. 2023-02-19 08:00
  2. 2025-08-13

一、Introduction #

想要优化性能,需要先进行性能测试。

压力测试工具

  • ab
  • webbench

找到性能瓶颈所在

  • top:CPU、内存
  • iostat:硬盘

CPU/内存/硬盘/网卡/后端

二、调优方向 #

  1. 代码优化
  2. 多进程优化
  3. 架构优化
  4. 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

  1. 性能优化:对于计算密集型任务,C/C++ 通常比 JavaScript 更快
  2. 系统级访问:直接调用操作系统 API 或硬件功能
  3. 重用现有库:集成已有的 C/C++ 库到 Node.js 生态
  4. 多线程处理:突破 Node.js 单线程模型的限制

主要开发方式 #

  1. Node-API (N-API)
  2. node-gyp
  3. 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 变化较大)

开发工具链 #

  1. node-gyp
  2. binding.gyp
  3. Rust Neon
  4. WebAssembly:对于纯计算任务,可以考虑使用 WASM
  5. 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]

  1. 纯计算任务:优先考虑WebAssembly
  2. 需要原生扩展
    • Rust技术栈 → NAPI-RS 或 Neon
    • 跨语言需求 → Puerts
  3. 全新项目:可评估Bun/Deno替代Node.js
  4. 企业级应用:Node-API或NAPI-RS保证长期兼容性

Reference #

Cluster API