本文介绍Node.js运行环境的链路追踪相关内容。
背景信息
阿里云链路追踪服务(Tracing Analysis)基于OpenTracing标准,兼容开源社区,为分布式应用的开发者提供了完整地分布式调用链查询和诊断、分布式拓扑动态发现、应用性能实时汇总等功能。
函数计算与链路追踪集成后,支持使用Jaeger SDK和OpenTelemetry上传链路信息,使您能够跟踪函数的执行,帮助您快速分析和诊断Serverless架构下的性能瓶颈,提高Serverless场景的开发诊断效率。
功能简介
您可以在函数计算控制台配置链路追踪。具体操作,请参见配置链路追踪。
为服务开启链路追踪后,函数计算会自动记录请求在系统侧的耗时,包含冷启动耗时、Initializer函数的耗时和函数的执行时间等。关于下图中系统Span的说明,请参见Span名称说明。
如您还需查看函数内业务侧的耗时,例如,在函数内访问RDS,NAS等服务的耗时,可以通过创建自定义Span来实现。
示例代码
函数计算的链路分析基于OpenTracing协议的Jaeger实现,Node.js运行时提供以下两种方式自定义Span。
使用OpenTelemetry(推荐)
在Node.js语言的代码中,您可以通过OpenTelemetry SDK手动埋点将数据上报到链路追踪服务端。完整的示例代码,请参见nodejs-tracing-openTelemetry。
示例代码解析如下。
- 在工程目录中配置依赖文件package.json。
"dependencies": { "@opentelemetry/api": "^1.0.2", "@opentelemetry/exporter-jaeger": "0.25.0", "@opentelemetry/exporter-zipkin": "0.25.0", "@opentelemetry/instrumentation": "0.25.0", "@opentelemetry/instrumentation-http": "0.25.0", "@opentelemetry/resources": "0.25.0", "@opentelemetry/semantic-conventions": "0.25.0", "@opentelemetry/sdk-trace-node": "0.25.0", "@opentelemetry/sdk-trace-base": "0.25.0" }
- 上报数据到链路追踪服务端。
module.exports.handler = function(event, context, callback) { tracer = require('./tracer')('fc-tracer',context.tracing.jaegerEndpoint); var spanContext = contextFromString( context.tracing.openTracingSpanContext); startMySpan(spanContext); callback(null,'success'); }
- 创建一个
tracer
对象,用于创建Span。module.exports = (serviceName,endpoint) => { const provider = new NodeTracerProvider({ resource: new Resource({ [SemanticResourceAttributes.SERVICE_NAME]: serviceName, }), }); let exporter = new JaegerExporter({endpoint:endpoint}); provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); provider.register(); registerInstrumentations({ instrumentations: [ new HttpInstrumentation(), ], }); return opentelemetry.trace.getTracer('http-example'); };
- 获取函数计算上下文的Tracing信息,转换为SpanContext。
function contextFromString(value){ const arr = value.split(`:`); const spanContext={ traceId:`0000000000000000`+arr[0], spanId:arr[1], traceFlags:api.TraceFlags.SAMPLED, isRemote:true } return spanContext; }
- 创建
tracer
并通过转换的Context创建子Span。每一个Span代表调用链中被命名并计时的连续性执行片段,您也可以基于该Span继续创建子Span。function startMySpan(spanContext){ var FcSpan=api.trace.wrapSpanContext(spanContext); var ctx = api.trace.setSpan(api.ROOT_CONTEXT,FcSpan); tracer.startActiveSpan("fc-operation",undefined,ctx,parentSpan => { parentSpan.setAttribute("version","fc-v1"); sleep(150); child(); parentSpan.end() }) } function child(){ tracer.startActiveSpan("fc-operation-child",span =>{ sleep(100); span.addEvent("timeout"); span.end(); }) }
使用Jaeger SDK
您可以通过Jaeger SDK埋点,将数据上报到链路追踪服务端。完整的示例代码,请参见nodejs-tracing。
示例代码解析如下。
- 在工程目录中配置依赖文件package.json。
"dependencies": { "jaeger-client": "^3.19.0" }
- 上报数据到链路追踪服务端。
module.exports.handler = function(event, context, callback) { tracer=newTracer(context); var invokeSpanContext = spanContext.fromString(context.tracing.openTracingSpanContext); startMySpan(invokeSpanContext); callback(null,'success') }
- 根据上下文的Tracing信息,创建一个
tracer
对象。function newTracer(context){ var config = { serviceName: 'fc-tracer', reporter: { // Provide the traces endpoint; this forces the client to connect directly to the Collector and send spans over HTTP collectorEndpoint: context.tracing.jaegerEndpoint, flushIntervalMs: 10, }, sampler: { type: "const", param: 1 }, }; var options = { tags: { 'version': 'fc-v1', }, }; var tracer = initTracer(config, options); return tracer }
- 转换SpanContext对象并创建自定义Span,您也可以基于该Span继续创建子Span。
function startMySpan(spanContext){ var parentSpan = tracer.startSpan("fc-operation", { childOf: spanContext }); sleep(150); child(parentSpan.context()) parentSpan.finish(); } function child(spanContext){ var childSpan = tracer.startSpan("fc-operation-child", { childOf: spanContext }); childSpan.log({event:"timeout"}); sleep(100); childSpan.finish(); }