鏈路追蹤
本文介紹Java 11運行環(huán)境的鏈路追蹤相關(guān)內(nèi)容。
背景信息
阿里云鏈路追蹤服務(wù)(Tracing Analysis)基于OpenTracing標準,兼容開源社區(qū),為分布式應(yīng)用的開發(fā)者提供了完整地分布式調(diào)用鏈查詢和診斷、分布式拓撲動態(tài)發(fā)現(xiàn)、應(yīng)用性能實時匯總等功能。
函數(shù)計算與鏈路追蹤集成后,支持使用Jaeger SDK和OpenTelemetry上傳鏈路信息,使您能夠跟蹤函數(shù)的執(zhí)行,幫助您快速分析和診斷Serverless架構(gòu)下的性能瓶頸,提高Serverless場景的開發(fā)診斷效率。
功能簡介
您可以在函數(shù)計算控制臺配置鏈路追蹤。具體操作,請參見配置鏈路追蹤。
如您還需查看函數(shù)內(nèi)業(yè)務(wù)側(cè)的耗時,例如,在函數(shù)內(nèi)訪問RDS,NAS等服務(wù)的耗時,可以通過創(chuàng)建自定義Span來實現(xiàn)。
示例代碼
函數(shù)計算的鏈路分析基于OpenTracing協(xié)議的Jaeger實現(xiàn),Java運行時提供以下兩種創(chuàng)建自定義Span的方式。
使用OpenTelemetry(推薦)
在Java語言的代碼中,您可以通過OpenTelemetry SDK手動埋點將數(shù)據(jù)上報到鏈路追蹤服務(wù)端。完整的示例代碼,請參見java-tracing-openTelemetry。
示例代碼解析如下。
在pom.xml文件中添加依賴。
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>com.aliyun.fc.runtime</groupId> <artifactId>fc-java-core</artifactId> <version>1.4.1</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-api</artifactId> <version>1.19.0</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk</artifactId> <version>1.19.0</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-semconv</artifactId> <version>1.19.0-alpha</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-jaeger-thrift</artifactId> <version>1.19.0</version> </dependency> <dependency> <groupId>io.jaegertracing</groupId> <artifactId>jaeger-thrift</artifactId> <version>1.8.1</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.6.6</version> </dependency> </dependencies>
上報數(shù)據(jù)到鏈路追蹤服務(wù)端。
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { String endpoint = context.getTracing().getJaegerEndpoint(); try { ExampleConfiguration.initOpenTelemetry(endpoint); } catch (TTransportException e) { throw new RuntimeException(e); } SpanContext spanContext = contextFromString(context.getTracing().getSpanContext()); startMySpan(io.opentelemetry.context.Context.current().with(Span.wrap(spanContext))); }
創(chuàng)建一個全局
OpenTelemetry
對象,提供對Tracers的訪問。static OpenTelemetry initOpenTelemetry(String jaegerEndpoint) throws TTransportException { // 導(dǎo)出traces到Jaeger JaegerThriftSpanExporter jaegerExporter = JaegerThriftSpanExporter.builder() .setThriftSender(new Builder(jaegerEndpoint).build()) .setEndpoint(jaegerEndpoint) .build(); Resource serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "otel-jaeger-example")); // 設(shè)置由Jaeger Exporter處理Span SdkTracerProvider tracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(jaegerExporter)) .setResource(Resource.getDefault().merge(serviceNameResource)) .build(); OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).buildAndRegisterGlobal(); // JVM退出時關(guān)閉SDK Runtime.getRuntime().addShutdownHook(new Thread(tracerProvider::close)); return openTelemetry; }
獲取上下文的Tracing信息,轉(zhuǎn)換為SpanContext。
SpanContext contextFromString(String value) throws IOException { { if (value != null && !value.equals("")) { String[] parts = value.split(":"); if (parts.length != 4) { throw new RuntimeException(value); } else { String traceId = parts[0]; if (traceId.length() <= 32 && traceId.length() >= 1) { return SpanContext.createFromRemoteParent("0000000000000000"+parts[0], parts[1], TraceFlags.getSampled(), TraceState.getDefault()); } else { throw new RuntimeException("Trace id [" + traceId + "] length is not withing 1 and 32"); } } } else { throw new RuntimeException(); } } }
創(chuàng)建
tracer
并通過轉(zhuǎn)換的Context創(chuàng)建子Span。每一個Span代表調(diào)用鏈中被命名并計時的連續(xù)性執(zhí)行片段,您也可以基于該Span繼續(xù)創(chuàng)建子Span。void startMySpan(io.opentelemetry.context.Context ctx){ Tracer tracer = GlobalOpenTelemetry.getTracer("fc-Trace"); Span parentSpan = tracer.spanBuilder("fc-operation").setParent(ctx).startSpan(); parentSpan.setAttribute("version","fc-v1"); try { TimeUnit.MILLISECONDS.sleep(150); } catch (InterruptedException e) { throw new RuntimeException(e); } child(parentSpan.storeInContext(ctx)); parentSpan.end(); } void child(io.opentelemetry.context.Context ctx){ Tracer tracer = GlobalOpenTelemetry.getTracer("fc-Trace"); Span childSpan = tracer.spanBuilder("fc-operation-child").setParent(ctx).startSpan(); childSpan.addEvent("timeout"); try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } childSpan.end(); }
使用Jaeger SDK
您可以通過Jaeger SDK埋點,將數(shù)據(jù)上報到鏈路追蹤服務(wù)端。完整的示例代碼,請參見java-tracing。
示例代碼解析如下。
在pom.xml文件中添加依賴。
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>com.aliyun.fc.runtime</groupId> <artifactId>fc-java-core</artifactId> <version>1.4.1</version> </dependency> <dependency> <groupId>io.jaegertracing</groupId> <artifactId>jaeger-client</artifactId> <version>1.8.1</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.6.6</version> </dependency> </dependencies>
上報數(shù)據(jù)到鏈路追蹤服務(wù)端。
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { registerTracer(context); JaegerSpanContext spanContext = contextFromString(context.getTracing().getSpanContext()); startMySpan(spanContext); }
根據(jù)上下文的Tracing信息創(chuàng)建一個
tracer
。void registerTracer(Context context){ io.jaegertracing.Configuration config = new io.jaegertracing.Configuration("FCTracer"); io.jaegertracing.Configuration.SenderConfiguration sender = new io.jaegertracing.Configuration.SenderConfiguration(); sender.withEndpoint(context.getTracing().getJaegerEndpoint()); config.withSampler(new io.jaegertracing.Configuration.SamplerConfiguration().withType("const").withParam(1)); config.withReporter(new io.jaegertracing.Configuration.ReporterConfiguration().withSender(sender).withMaxQueueSize(10000)); GlobalTracer.register(config.getTracer()); }
轉(zhuǎn)換spanContext并創(chuàng)建自定義Span,您也可以基于該Span繼續(xù)創(chuàng)建子Span。
static JaegerSpanContext contextFromString(String value) throws MalformedTracerStateStringException, EmptyTracerStateStringException { if (value != null && !value.equals("")) { String[] parts = value.split(":"); if (parts.length != 4) { throw new MalformedTracerStateStringException(value); } else { String traceId = parts[0]; if (traceId.length() <= 32 && traceId.length() >= 1) { return new JaegerSpanContext(0L, (new BigInteger(traceId, 16)).longValue(), (new BigInteger(parts[1], 16)).longValue(), (new BigInteger(parts[2], 16)).longValue(), (new BigInteger(parts[3], 16)).byteValue()); } else { throw new TraceIdOutOfBoundException("Trace id [" + traceId + "] length is not withing 1 and 32"); } } } else { throw new EmptyTracerStateStringException(); } }