@loongsuite/cms_context
v1.0.0
Published
## 简介
Readme
@loongsuite/cms_context
简介
CMS Context 提供基于 Node.js AsyncLocalStorage 和 async_hooks 的上下文管理器实现,用于在异步调用链中传递和保持上下文信息。这是实现分布式追踪上下文传播的关键组件。
特性
- 🔄 异步上下文: 基于 AsyncLocalStorage 的异步上下文管理
- 🎯 自动传播: 自动在异步调用中传播上下文
- ⚡ 高性能: 利用 Node.js 原生 async_hooks 机制
- 🔧 灵活配置: 支持多种上下文管理器实现
- 🛡️ 类型安全: 完整的 TypeScript 类型支持
安装
# 使用 anpm (推荐)
anpm add @loongsuite/cms_context
# 或使用 npm
npm install @loongsuite/cms_context核心概念
上下文管理器
上下文管理器负责在异步调用中维护和传递上下文信息。CMS Context 提供了两种实现:
- AsyncLocalStorageContextManager: 基于 AsyncLocalStorage(推荐)
- AsyncHooksContextManager: 基于 async_hooks
AsyncLocalStorageContextManager
这是默认的上下文管理器实现,基于 Node.js 12.17+ 的 AsyncLocalStorage API。
import { AsyncLocalStorageContextManager } from '@loongsuite/cms_context';
const contextManager = new AsyncLocalStorageContextManager();
// 获取当前上下文
const currentContext = contextManager.active();
// 在指定上下文中执行代码
const newContext = { traceId: 'abc123', spanId: 'def456' };
contextManager.with(newContext, () => {
// 在此作用域内,上下文为新上下文
const activeContext = contextManager.active();
console.log(activeContext); // { traceId: 'abc123', spanId: 'def456' }
// 异步操作也会保持上下文
setTimeout(() => {
const asyncContext = contextManager.active();
console.log(asyncContext); // 仍然是 { traceId: 'abc123', spanId: 'def456' }
}, 100);
});AsyncHooksContextManager
基于 async_hooks 的上下文管理器,兼容更老的 Node.js 版本。
import { AsyncHooksContextManager } from '@loongsuite/cms_context';
const contextManager = new AsyncHooksContextManager();
// 使用方式与 AsyncLocalStorageContextManager 相同
const context = { userId: 'user123', requestId: 'req456' };
contextManager.with(context, () => {
// 业务逻辑
processAsyncOperation();
});主要导出
import {
AsyncLocalStorageContextManager,
AsyncHooksContextManager,
IContextManager,
Context
} from '@loongsuite/cms_context';使用示例
基本用法
import { AsyncLocalStorageContextManager } from '@loongsuite/cms_context';
const contextManager = new AsyncLocalStorageContextManager();
// 设置初始上下文
const initialContext = {
traceId: 'trace-123',
spanId: 'span-456',
userId: 'user-789'
};
contextManager.with(initialContext, () => {
console.log('Initial context:', contextManager.active());
// 嵌套的异步操作
setTimeout(() => {
console.log('Async context:', contextManager.active());
// 更深层的异步操作
Promise.resolve().then(() => {
console.log('Promise context:', contextManager.active());
});
}, 100);
});与追踪器集成
import { AsyncLocalStorageContextManager } from '@loongsuite/cms_context';
import { TracerManager } from '@loongsuite/cms_trace';
const contextManager = new AsyncLocalStorageContextManager();
const tracerManager = new TracerManager({
contextManager,
// ... 其他配置
});
const tracer = tracerManager.getTracer('my-service');
// 创建 span 并设置到上下文
const span = tracer.startSpan('operation');
const contextWithSpan = tracerManager.setSpan(contextManager.active(), span);
contextManager.with(contextWithSpan, () => {
// 在此上下文中,可以获取到当前的 span
const currentSpan = tracerManager.getActiveSpan(contextManager.active());
console.log('Current span:', currentSpan);
// 异步操作会保持 span 上下文
setTimeout(() => {
const asyncSpan = tracerManager.getActiveSpan(contextManager.active());
console.log('Async span:', asyncSpan); // 仍然是同一个 span
}, 100);
});上下文绑定
import { AsyncLocalStorageContextManager } from '@loongsuite/cms_context';
const contextManager = new AsyncLocalStorageContextManager();
// 绑定上下文到函数
const context = { requestId: 'req-123' };
const boundFunction = contextManager.bind(context, (message: string) => {
const activeContext = contextManager.active();
console.log(`[${activeContext.requestId}] ${message}`);
});
// 即使在不同上下文中调用,也会使用绑定的上下文
contextManager.with({ requestId: 'req-456' }, () => {
boundFunction('Hello World'); // 输出: [req-123] Hello World
});错误处理
import { AsyncLocalStorageContextManager } from '@loongsuite/cms_context';
const contextManager = new AsyncLocalStorageContextManager();
const context = { operationId: 'op-123' };
try {
contextManager.with(context, () => {
// 可能抛出错误的操作
throw new Error('Something went wrong');
});
} catch (error) {
// 错误处理时仍然可以访问上下文
const errorContext = contextManager.active();
console.log(`Error in context ${errorContext.operationId}:`, error.message);
}性能考虑
AsyncLocalStorage vs AsyncHooks
- AsyncLocalStorage: 性能更好,但需要 Node.js 12.17+
- AsyncHooks: 兼容性更好,但性能稍差
最佳实践
- 优先使用 AsyncLocalStorageContextManager
- 避免频繁的上下文切换
- 合理使用上下文绑定
// 好的做法:批量操作
contextManager.with(context, () => {
// 执行多个相关操作
operation1();
operation2();
operation3();
});
// 避免:频繁切换上下文
contextManager.with(context1, () => operation1());
contextManager.with(context2, () => operation2());
contextManager.with(context3, () => operation3());与 NodeSDK 集成
NodeSDK 默认使用 AsyncLocalStorageContextManager,通常无需手动配置:
import { NodeSDK } from '@loongsuite/cms_node_sdk';
const sdk = new NodeSDK({
serviceName: 'my-service',
// contextManager 会自动使用 AsyncLocalStorageContextManager
});
sdk.start();
// 获取上下文管理器
const contextManager = sdk.getContextManager();
console.log(contextManager instanceof AsyncLocalStorageContextManager); // true故障排除
常见问题
- 上下文丢失: 确保在正确的异步边界内使用上下文
- 性能问题: 考虑使用 AsyncLocalStorageContextManager
- 内存泄漏: 及时清理不需要的上下文
调试技巧
import { AsyncLocalStorageContextManager } from '@loongsuite/cms_context';
const contextManager = new AsyncLocalStorageContextManager();
// 添加调试信息
const debugContext = {
...context,
_debug: {
timestamp: Date.now(),
stack: new Error().stack
}
};
contextManager.with(debugContext, () => {
console.log('Context debug info:', contextManager.active()._debug);
});依赖
@loongsuite/cms_core: 核心接口和类型定义
许可证
MIT License
