Node.js 适配器
Node.js 平台适配器为 Node.js 服务器环境提供支持,适用于游戏服务器、计算服务器或其他需要 ECS 架构的服务器应用。
- ✅ Worker: 支持(通过
worker_threads模块) - ❌ SharedArrayBuffer: 支持(Node.js 16.17.0+)
- ✅ Transferable Objects: 完全支持
- ✅ 高精度时间: 使用
process.hrtime.bigint() - ✅ 设备信息: 完整的系统和进程信息
import { worker_threads, Worker, isMainThread, parentPort } from 'worker_threads';import * as os from 'os';import * as process from 'process';import * as fs from 'fs';import * as path from 'path';import type { IPlatformAdapter, PlatformWorker, WorkerCreationOptions, PlatformConfig, NodeDeviceInfo} from '@esengine/ecs-framework';
/** * Node.js 平台适配器 * 支持 Node.js 服务器环境 */export class NodeAdapter implements IPlatformAdapter { public readonly name = 'nodejs'; public readonly version: string;
constructor() { this.version = process.version; }
/** * 检查是否支持Worker */ public isWorkerSupported(): boolean { try { // 检查 worker_threads 模块是否可用 return typeof worker_threads !== 'undefined' && typeof Worker !== 'undefined'; } catch { return false; } }
/** * 检查是否支持SharedArrayBuffer */ public isSharedArrayBufferSupported(): boolean { // Node.js 支持 SharedArrayBuffer return typeof SharedArrayBuffer !== 'undefined'; }
/** * 获取硬件并发数(CPU核心数) */ public getHardwareConcurrency(): number { return os.cpus().length; }
/** * 创建Worker */ public createWorker(script: string, options: WorkerCreationOptions = {}): PlatformWorker { if (!this.isWorkerSupported()) { throw new Error('Node.js环境不支持Worker Threads'); }
try { return new NodeWorker(script, options); } catch (error) { throw new Error(`创建Node.js Worker失败: ${(error as Error).message}`); } }
/** * 创建SharedArrayBuffer */ public createSharedArrayBuffer(length: number): SharedArrayBuffer | null { if (!this.isSharedArrayBufferSupported()) { return null; }
try { return new SharedArrayBuffer(length); } catch (error) { console.warn('SharedArrayBuffer创建失败:', error); return null; } }
/** * 获取高精度时间戳(纳秒) */ public getHighResTimestamp(): number { // 返回毫秒,与浏览器 performance.now() 保持一致 return Number(process.hrtime.bigint()) / 1000000; }
/** * 获取平台配置 */ public getPlatformConfig(): PlatformConfig { return { maxWorkerCount: this.getHardwareConcurrency(), supportsModuleWorker: true, // Node.js 支持 ES 模块 supportsTransferableObjects: true, maxSharedArrayBufferSize: this.getMaxSharedArrayBufferSize(), workerScriptPrefix: '', limitations: { noEval: false, // Node.js 支持 eval requiresWorkerInit: false }, extensions: { platform: 'nodejs', nodeVersion: process.version, v8Version: process.versions.v8, uvVersion: process.versions.uv, zlibVersion: process.versions.zlib, opensslVersion: process.versions.openssl, architecture: process.arch, endianness: os.endianness(), pid: process.pid, ppid: process.ppid } }; }
/** * 获取Node.js设备信息 */ public getDeviceInfo(): NodeDeviceInfo { const cpus = os.cpus(); const networkInterfaces = os.networkInterfaces(); const userInfo = os.userInfo();
return { // 系统信息 platform: os.platform(), arch: os.arch(), type: os.type(), release: os.release(), version: os.version(), hostname: os.hostname(),
// CPU信息 cpus: cpus.map(cpu => ({ model: cpu.model, speed: cpu.speed, times: cpu.times })),
// 内存信息 totalMemory: os.totalmem(), freeMemory: os.freemem(), usedMemory: os.totalmem() - os.freemem(),
// 负载信息 loadAverage: os.loadavg(),
// 网络接口 networkInterfaces: Object.fromEntries( Object.entries(networkInterfaces).map(([name, interfaces]) => [ name, (interfaces || []).map(iface => ({ address: iface.address, netmask: iface.netmask, family: iface.family as 'IPv4' | 'IPv6', mac: iface.mac, internal: iface.internal, cidr: iface.cidr, scopeid: iface.scopeid })) ]) ),
// 进程信息 process: { pid: process.pid, ppid: process.ppid, version: process.version, versions: process.versions, uptime: process.uptime() },
// 用户信息 userInfo: { uid: userInfo.uid, gid: userInfo.gid, username: userInfo.username, homedir: userInfo.homedir, shell: userInfo.shell } }; }
/** * 获取SharedArrayBuffer最大大小限制 */ private getMaxSharedArrayBufferSize(): number { const totalMemory = os.totalmem(); // 限制为系统总内存的50% return Math.floor(totalMemory * 0.5); }}
/** * Node.js Worker封装 */class NodeWorker implements PlatformWorker { private _state: 'running' | 'terminated' = 'running'; private worker: Worker; private isTemporaryFile: boolean = false; private scriptPath: string;
constructor(script: string, options: WorkerCreationOptions = {}) { try { // 判断 script 是文件路径还是脚本内容 if (this.isFilePath(script)) { // 直接使用文件路径 this.scriptPath = script; this.isTemporaryFile = false; } else { // 将脚本内容写入临时文件 this.scriptPath = this.writeScriptToFile(script, options.name); this.isTemporaryFile = true; }
// 创建Worker this.worker = new Worker(this.scriptPath, { // Node.js Worker options workerData: options.name ? { name: options.name } : undefined });
} catch (error) { throw new Error(`创建Node.js Worker失败: ${(error as Error).message}`); } }
/** * 判断是否为文件路径 */ private isFilePath(script: string): boolean { // 检查是否看起来像文件路径 return (script.endsWith('.js') || script.endsWith('.mjs') || script.endsWith('.ts')) && !script.includes('\n') && !script.includes(';') && script.length < 500; // 文件路径通常不会太长 }
/** * 将脚本内容写入临时文件 */ private writeScriptToFile(script: string, name?: string): string { const tmpDir = os.tmpdir(); const fileName = name ? `worker-${name}-${Date.now()}.js` : `worker-${Date.now()}.js`; const filePath = path.join(tmpDir, fileName);
try { fs.writeFileSync(filePath, script, 'utf8'); return filePath; } catch (error) { throw new Error(`写入Worker脚本文件失败: ${(error as Error).message}`); } }
public get state(): 'running' | 'terminated' { return this._state; }
public postMessage(message: any, transfer?: Transferable[]): void { if (this._state === 'terminated') { throw new Error('Worker已被终止'); }
try { if (transfer && transfer.length > 0) { // Node.js Worker 支持 Transferable Objects this.worker.postMessage(message, transfer); } else { this.worker.postMessage(message); } } catch (error) { throw new Error(`发送消息到Node.js Worker失败: ${(error as Error).message}`); } }
public onMessage(handler: (event: { data: any }) => void): void { this.worker.on('message', (data: any) => { handler({ data }); }); }
public onError(handler: (error: ErrorEvent) => void): void { this.worker.on('error', (error: Error) => { // 将 Error 转换为 ErrorEvent 格式 const errorEvent = { message: error.message, filename: '', lineno: 0, colno: 0, error: error } as ErrorEvent; handler(errorEvent); }); }
public terminate(): void { if (this._state === 'running') { try { this.worker.terminate(); this._state = 'terminated';
// 清理临时脚本文件 this.cleanupScriptFile(); } catch (error) { console.error('终止Node.js Worker失败:', error); } } }
/** * 清理临时脚本文件 */ private cleanupScriptFile(): void { // 只清理临时创建的文件,不清理用户提供的文件路径 if (this.scriptPath && this.isTemporaryFile) { try { fs.unlinkSync(this.scriptPath); } catch (error) { console.warn('清理Worker脚本文件失败:', error); } } }}1. 复制代码
Section titled “1. 复制代码”将上述代码复制到你的项目中,例如 src/platform/NodeAdapter.ts。
2. 注册适配器
Section titled “2. 注册适配器”import { PlatformManager } from '@esengine/ecs-framework';import { NodeAdapter } from './platform/NodeAdapter';
// 检查是否在Node.js环境if (typeof process !== 'undefined' && process.versions && process.versions.node) { const nodeAdapter = new NodeAdapter(); PlatformManager.getInstance().registerAdapter(nodeAdapter);}3. 使用 WorkerEntitySystem
Section titled “3. 使用 WorkerEntitySystem”Node.js 适配器与 WorkerEntitySystem 配合使用,框架会自动处理 Worker 脚本的创建:
import { WorkerEntitySystem, Matcher } from '@esengine/ecs-framework';import * as os from 'os';
class PhysicsSystem extends WorkerEntitySystem { constructor() { super(Matcher.all(Transform, Velocity), { enableWorker: true, workerCount: os.cpus().length, // 使用所有CPU核心 useSharedArrayBuffer: true, systemConfig: { gravity: 9.8 } }); }
protected getDefaultEntityDataSize(): number { return 6; // x, y, vx, vy, mass, radius }
protected extractEntityData(entity: Entity): PhysicsData { const transform = entity.getComponent(Transform); const velocity = entity.getComponent(Velocity); return { x: transform.x, y: transform.y, vx: velocity.x, vy: velocity.y, mass: 1, radius: 10 }; }
// 这个函数会被自动序列化并在Worker中执行 protected workerProcess(entities, deltaTime, config) { return entities.map(entity => { // 应用重力 entity.vy += config.gravity * deltaTime;
// 更新位置 entity.x += entity.vx * deltaTime; entity.y += entity.vy * deltaTime;
return entity; }); }
protected applyResult(entity: Entity, result: PhysicsData): void { const transform = entity.getComponent(Transform); const velocity = entity.getComponent(Velocity);
transform.x = result.x; transform.y = result.y; velocity.x = result.vx; velocity.y = result.vy; }}
interface PhysicsData { x: number; y: number; vx: number; vy: number; mass: number; radius: number;}4. 获取系统信息
Section titled “4. 获取系统信息”const manager = PlatformManager.getInstance();if (manager.hasAdapter()) { const adapter = manager.getAdapter(); const deviceInfo = adapter.getDeviceInfo();
console.log('Node.js版本:', deviceInfo.process?.version); console.log('CPU核心数:', deviceInfo.cpus?.length); console.log('总内存:', Math.round(deviceInfo.totalMemory! / 1024 / 1024), 'MB'); console.log('可用内存:', Math.round(deviceInfo.freeMemory! / 1024 / 1024), 'MB');}官方文档参考
Section titled “官方文档参考”Node.js Worker Threads 相关官方文档:
重要注意事项
Section titled “重要注意事项”Worker Threads 要求
Section titled “Worker Threads 要求”- Node.js版本: 需要 Node.js 10.5.0+ (建议 12+)
- 模块类型: 支持 CommonJS 和 ES 模块
- 线程限制: 理论上无限制,但建议不超过 CPU 核心数的 2 倍
性能优化建议
Section titled “性能优化建议”1. Worker 池管理
Section titled “1. Worker 池管理”class ServerPhysicsSystem extends WorkerEntitySystem { constructor() { const cpuCount = os.cpus().length; super(Matcher.all(Transform, Velocity), { enableWorker: true, workerCount: Math.min(cpuCount * 2, 16), // 最多16个Worker entitiesPerWorker: 1000, // 每个Worker处理1000个实体 useSharedArrayBuffer: true, systemConfig: { gravity: 9.8, timeStep: 1/60 } }); }}2. 内存管理
Section titled “2. 内存管理”class MemoryMonitor { public static checkMemoryUsage(): void { const used = process.memoryUsage();
console.log('内存使用情况:'); console.log(` RSS: ${Math.round(used.rss / 1024 / 1024)} MB`); console.log(` Heap Used: ${Math.round(used.heapUsed / 1024 / 1024)} MB`); console.log(` Heap Total: ${Math.round(used.heapTotal / 1024 / 1024)} MB`); console.log(` External: ${Math.round(used.external / 1024 / 1024)} MB`);
// 内存使用率过高时触发警告 if (used.heapUsed > used.heapTotal * 0.9) { console.warn('内存使用率过高,建议优化或重启'); } }}
// 定期检查内存使用setInterval(() => { MemoryMonitor.checkMemoryUsage();}, 30000); // 每30秒检查一次3. 服务器环境优化
Section titled “3. 服务器环境优化”// 设置进程标题process.title = 'ecs-game-server';
// 处理未捕获异常process.on('uncaughtException', (error) => { console.error('未捕获异常:', error); process.exit(1);});
process.on('unhandledRejection', (reason, promise) => { console.error('未处理的Promise拒绝:', reason);});
// 优雅关闭process.on('SIGTERM', () => { console.log('收到SIGTERM信号,正在关闭服务器...'); // 清理资源 process.exit(0);});// 检查Node.js环境支持情况const adapter = new NodeAdapter();console.log('Node.js版本:', adapter.version);console.log('Worker支持:', adapter.isWorkerSupported());console.log('SharedArrayBuffer支持:', adapter.isSharedArrayBufferSupported());console.log('CPU核心数:', adapter.getHardwareConcurrency());
// 获取详细配置const config = adapter.getPlatformConfig();console.log('平台配置:', JSON.stringify(config, null, 2));
// 系统资源监控const deviceInfo = adapter.getDeviceInfo();console.log('系统负载:', deviceInfo.loadAverage);console.log('网络接口:', Object.keys(deviceInfo.networkInterfaces!));