このトピックでは、Java 11ランタイム環境でのトレース分析機能について説明します。
背景
Alibaba Cloud Tracing AnalysisはOpenTracing標準を採用しており、オープンソースコミュニティと互換性があります。 Tracing Analysisは、分散アプリケーションの開発者にさまざまな機能を提供します。 たとえば、開発者は分散トレースのクエリと診断、分散トポロジの動的な検出、アプリケーションのパフォーマンスのリアルタイムな要約を行うことができます。
Function ComputeはTracing Analysisと統合します。 Jaeger SDKまたはOpenTelemetryを使用してトレース情報をアップロードし、関数の呼び出しを追跡できます。 Tracing Analysisは、サーバーレスアーキテクチャのパフォーマンスボトルネックの分析と診断に役立ちます。 これにより、サーバーレスシナリオでの開発と診断の効率が向上します。
概要
Tracing Analysisは、Function Computeコンソールで設定できます。 詳細については、「トレース分析の有効化」をご参照ください。
サービスのTracing Analysisを有効にすると、Function Computeはシステムで消費された時間を自動的に記録します。 時間は、コールドスタート時間、イニシャライザ関数の実行時間、及び関数の実行時間を含む。 次の図のシステムスパンの詳細については、「スパン名の説明」をご参照ください。
ビジネス側の関数で消費された時間を記録できます。 たとえば、[カスタムスパンの作成] を使用して、関数内でApsaraDB RDSやApsara File Storage NASなどのサービスにアクセスするのにかかる時間を記録できます。
サンプルコード
Javaランタイム環境でのFunction Computeのトレース分析では、OpenTracing仕様のJaeger実装に基づいて、次の方法を使用してカスタムスパンを作成できます。
OpenTelemetry用SDKの使用 (推奨)
Javaコードでは、SDK for OpenTelemetryを使用してコードインストルメンテーションデータを実行し、そのデータをTracing Analysisに報告できます。 完全なサンプルコードについては、「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>
データをTracing Analysisに報告します。
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))); }
トレーサーへのアクセスを提供するグローバル
OpenTelemetry
オブジェクトを作成します。static OpenTelemetry initOpenTelemetry(String jaegerEndpoint) throws TTransportException { // Export traces to 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")); // Specify a Jaeger exporter as the processor of the span. SdkTracerProvider tracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(jaegerExporter)) .setResource(Resource.getDefault().merge(serviceNameResource)) .build(); OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).buildAndRegisterGlobal(); // Disable the SDK when the Java virtual machine (JVM) exits. Runtime.getRuntime().addShutdownHook(new Thread(tracerProvider::close)); return openTelemetry; }
Contextのトレース情報を取得し、トレース情報を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(); } } }
トレーサー
を作成し、変換されたコンテキストを使用して子スパンを作成します。 スパンは、呼び出しトレースで名前が付けられ、時間が設定された継続的に実行可能なコードスニペットを表します。 スパンに基づいて子スパンを作成することもできます。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を使用して、コードのインストルメンテーションを実行し、データをTracing Analysisに報告できます。 完全なサンプルコードについては、「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>
データをTracing Analysisに報告します。
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { registerTracer(context); JaegerSpanContext spanContext = contextFromString(context.getTracing().getSpanContext()); startMySpan(spanContext); }
コンテキストのトレース情報に基づいて、
トレーサー
オブジェクトを作成します。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()); }
SpanContextを変換し、カスタムスパンを作成します。 スパンに基づいて子スパンを作成することもできます。
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(); } }