本文介紹Node.js運(yùn)行環(huán)境的鏈路追蹤相關(guān)內(nèi)容。
背景信息
阿里云鏈路追蹤服務(wù)(Tracing Analysis)基于OpenTracing標(biāo)準(zhǔn),兼容開源社區(qū),為分布式應(yīng)用的開發(fā)者提供了完整地分布式調(diào)用鏈查詢和診斷、分布式拓?fù)鋭?dòng)態(tài)發(fā)現(xiàn)、應(yīng)用性能實(shí)時(shí)匯總等功能。
函數(shù)計(jì)算與鏈路追蹤集成后,支持使用Jaeger SDK和OpenTelemetry上傳鏈路信息,使您能夠跟蹤函數(shù)的執(zhí)行,幫助您快速分析和診斷Serverless架構(gòu)下的性能瓶頸,提高Serverless場景的開發(fā)診斷效率。
功能簡介
您可以在函數(shù)計(jì)算控制臺(tái)配置鏈路追蹤。具體操作,請參見配置鏈路追蹤。
如您還需查看函數(shù)內(nèi)業(yè)務(wù)側(cè)的耗時(shí),例如,在函數(shù)內(nèi)訪問RDS,NAS等服務(wù)的耗時(shí),可以通過創(chuàng)建自定義Span來實(shí)現(xiàn)。
示例代碼
函數(shù)計(jì)算的鏈路分析基于OpenTracing協(xié)議的Jaeger實(shí)現(xiàn),Node.js運(yùn)行時(shí)提供以下兩種方式自定義Span。
使用OpenTelemetry(推薦)
在Node.js語言的代碼中,您可以通過OpenTelemetry SDK手動(dòng)埋點(diǎn)將數(shù)據(jù)上報(bào)到鏈路追蹤服務(wù)端。完整的示例代碼,請參見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" }
- 上報(bào)數(shù)據(jù)到鏈路追蹤服務(wù)端。
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'); }
- 創(chuàng)建一個(gè)
tracer
對象,用于創(chuàng)建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'); };
- 獲取函數(shù)計(jì)算上下文的Tracing信息,轉(zhuǎn)換為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; }
- 創(chuàng)建
tracer
并通過轉(zhuǎn)換的Context創(chuàng)建子Span。每一個(gè)Span代表調(diào)用鏈中被命名并計(jì)時(shí)的連續(xù)性執(zhí)行片段,您也可以基于該Span繼續(xù)創(chuàng)建子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埋點(diǎn),將數(shù)據(jù)上報(bào)到鏈路追蹤服務(wù)端。完整的示例代碼,請參見nodejs-tracing。
- 在工程目錄中配置依賴文件package.json。
"dependencies": { "jaeger-client": "^3.19.0" }
- 上報(bào)數(shù)據(jù)到鏈路追蹤服務(wù)端。
module.exports.handler = function(event, context, callback) { tracer=newTracer(context); var invokeSpanContext = spanContext.fromString(context.tracing.openTracingSpanContext); startMySpan(invokeSpanContext); callback(null,'success') }
- 根據(jù)上下文的Tracing信息,創(chuàng)建一個(gè)
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 }
- 轉(zhuǎn)換SpanContext對象并創(chuàng)建自定義Span,您也可以基于該Span繼續(xù)創(chuàng)建子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(); }