Latenz in einer Spanner-Komponente mit OpenTelemetry untersuchen

In diesem Thema wird beschrieben, wie Sie eine Spanner-Komponente untersuchen können um die Ursache der Latenz zu ermitteln und diese mit OpenTelemetry zu visualisieren. Einen allgemeinen Überblick über die Komponenten in diesem Thema finden Sie unter Latenzpunkte in einer Spanner-Anfrage.

OpenTelemetry ist ein Open-Source-Framework und -Toolkit für die Beobachtbarkeit, mit dem Telemetriedaten wie Traces, Messwerte und Logs erstellen und verwalten. Es ist das Ergebnis einer Zusammenführung zwischen OpenTracing und OpenCensus. Weitere Informationen Siehe Was ist OpenTelemetry?

Spanner-Clientbibliotheken bieten Messwerte und Traces mit der OpenTelemetry-Beobachtbarkeits-Framework. Folgen Sie der Anleitung unter Ermitteln Sie den Latenzpunkt, um den Komponenten oder Komponenten, die in Spanner Latenz anzeigen.

Hinweise

Bevor Sie mit der Erfassung von Latenzmesswerten beginnen, machen Sie sich mit den manuellen Abläufen Instrumentierung mit OpenTelemetry. Sie müssen das OpenTelemetry SDK konfigurieren. mit entsprechenden Optionen zum Exportieren Ihrer Telemetriedaten. Es gibt mehrere Optionen für den OpenTelemetry-Exporter verfügbar. Wir empfehlen die Verwendung von OpenTelemetry Protokollexporter (OTLP). Weitere Optionen sind die Verwendung eines OTel-Collectors mit Google Cloud Exporter oder Google Managed Service for Prometheus Exporter.

Wenn Sie Ihre Anwendung in Compute Engine ausführen, können Sie das Ops- Agent zum Erfassen von OpenTelemetry Protocol-Messwerten und -Traces. Weitere Informationen Siehe OTLP-Messwerte und -Traces erfassen.

Abhängigkeiten hinzufügen

Fügen Sie zum Konfigurieren des OpenTelemetry SDK und des OTLP-Exporters Folgendes hinzu: zu Ihrer Anwendung hinzufügen.

Java

<dependency>
  <groupId>com.google.cloud</groupId>
  <artifactId>google-cloud-spanner</artifactId>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-sdk</artifactId>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-sdk-metrics</artifactId>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-sdk-trace</artifactId>
</dependency>
<dependency>
  <groupId>io.opentelemetry</groupId>
  <artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>

Go

go.opentelemetry.io/otel v1.24.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.23.1
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.23.1
go.opentelemetry.io/otel/metric v1.24.0
go.opentelemetry.io/otel/sdk v1.23.1
go.opentelemetry.io/otel/sdk/metric v1.23.1

OpenTelemetry-Objekt einfügen

Erstellen Sie dann ein OpenTelemetry-Objekt mit dem OTLP-Exporter und fügen Sie das Objekt OpenTelemetry-Objekt mit SpannerOptions

Java

// Enable OpenTelemetry metrics and traces before Injecting OpenTelemetry
SpannerOptions.enableOpenTelemetryMetrics();
SpannerOptions.enableOpenTelemetryTraces();

// Create a new meter provider
SdkMeterProvider sdkMeterProvider = SdkMeterProvider.builder()
    // Use Otlp exporter or any other exporter of your choice.
    .registerMetricReader(
        PeriodicMetricReader.builder(OtlpGrpcMetricExporter.builder().build()).build())
    .build();

// Create a new tracer provider
SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
    // Use Otlp exporter or any other exporter of your choice.
    .addSpanProcessor(SimpleSpanProcessor.builder(OtlpGrpcSpanExporter
        .builder().build()).build())
        .build();

// Configure OpenTelemetry object using Meter Provider and Tracer Provider
OpenTelemetry openTelemetry = OpenTelemetrySdk.builder()
    .setMeterProvider(sdkMeterProvider)
    .setTracerProvider(sdkTracerProvider)
    .build();

// Inject OpenTelemetry object via Spanner options or register as GlobalOpenTelemetry.
SpannerOptions options = SpannerOptions.newBuilder()
    .setOpenTelemetry(openTelemetry)
    .build();
Spanner spanner = options.getService();

Go

// Ensure that your Go runtime version is supported by the OpenTelemetry-Go compatibility policy before enabling OpenTelemetry instrumentation.
// Refer to compatibility here https://github.com/googleapis/google-cloud-go/blob/main/debug.md#opentelemetry

import (
	"context"
	"fmt"
	"io"
	"log"
	"strconv"
	"strings"

	"cloud.google.com/go/spanner"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
	"go.opentelemetry.io/otel/metric"
	sdkmetric "go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/resource"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
	semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
	"google.golang.org/api/iterator"
)

func enableOpenTelemetryMetricsAndTraces(w io.Writer, db string) error {
	// db = `projects/<project>/instances/<instance-id>/database/<database-id>`
	ctx := context.Background()

	// Create a new resource to uniquely identify the application
	res, err := newResource()
	if err != nil {
		log.Fatal(err)
	}

	// Enable OpenTelemetry traces by setting environment variable GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING to the case-insensitive value "opentelemetry" before loading the client library.
	// Enable OpenTelemetry metrics before injecting meter provider.
	spanner.EnableOpenTelemetryMetrics()

	// Create a new tracer provider
	tracerProvider, err := getOtlpTracerProvider(ctx, res)
	defer tracerProvider.ForceFlush(ctx)
	if err != nil {
		log.Fatal(err)
	}
	// Register tracer provider as global
	otel.SetTracerProvider(tracerProvider)

	// Create a new meter provider
	meterProvider := getOtlpMeterProvider(ctx, res)
	defer meterProvider.ForceFlush(ctx)

	// Inject meter provider locally via ClientConfig when creating a spanner client or set globally via setMeterProvider.
	client, err := spanner.NewClientWithConfig(ctx, db, spanner.ClientConfig{OpenTelemetryMeterProvider: meterProvider})
	if err != nil {
		return err
	}
	defer client.Close()
	return nil
}

func getOtlpMeterProvider(ctx context.Context, res *resource.Resource) *sdkmetric.MeterProvider {
	otlpExporter, err := otlpmetricgrpc.New(ctx)
	if err != nil {
		log.Fatal(err)
	}
	meterProvider := sdkmetric.NewMeterProvider(
		sdkmetric.WithResource(res),
		sdkmetric.WithReader(sdkmetric.NewPeriodicReader(otlpExporter)),
	)
	return meterProvider
}

func getOtlpTracerProvider(ctx context.Context, res *resource.Resource) (*sdktrace.TracerProvider, error) {
	traceExporter, err := otlptracegrpc.New(ctx)
	if err != nil {
		return nil, err
	}

	tracerProvider := sdktrace.NewTracerProvider(
		sdktrace.WithResource(res),
		sdktrace.WithBatcher(traceExporter),
		sdktrace.WithSampler(sdktrace.AlwaysSample()),
	)

	return tracerProvider, nil
}

func newResource() (*resource.Resource, error) {
	return resource.Merge(resource.Default(),
		resource.NewWithAttributes(semconv.SchemaURL,
			semconv.ServiceName("otlp-service"),
			semconv.ServiceVersion("0.1.0"),
		))
}

Die Umlauflatenz des Clients erfassen und visualisieren

Die Client-Umlaufzeit ist der Zeitraum (in Millisekunden) zwischen erstes Byte der Spanner API-Anfrage, an die der Client sendet der Datenbank (über das GFE und das Spanner API-Front-End) und das letzte Byte der Antwort, die der Client von der Datenbank erhält.

Umlauflatenz des Clients erfassen

Der Messwert für die Umlauflatenz des Spanner-Clients wird nicht unterstützt mit OpenTelemetry. Sie können den Messwert mithilfe von OpenCensus mit einem Bridge und die Daten zu OpenTelemetry migrieren.

Client-Umlauflatenz visualisieren

Nach dem Abrufen der Messwerte können Sie die Client-Umlaufzeit in Cloud Monitoring

Hier ist ein Beispiel für eine Grafik, die die Latenz des 5. Perzentils für die der Client-Roundtrip-Latenz. Um die Perzentillatenz entweder 50. oder 99. Perzentil haben, verwenden Sie das Menü Aggregator.

Das Programm erstellt eine Ansicht mit dem Namen roundtrip_latency. Diese Zeichenfolge wird Teil des Messwerts, wenn er in Cloud Monitoring exportiert wird.

Umlauflatenz des Cloud Monitoring-Clients

GFE-Latenz erfassen und visualisieren

Die Latenz des Google Front End (GFE) ist die Zeitspanne (in Millisekunden) zwischen wenn das Google-Netzwerk einen Remote-Prozedur-Aufruf vom Client empfängt und wenn das GFE das erste Byte der Antwort empfängt.

GFE-Latenz erfassen

Sie können GFE-Latenzmesswerte erfassen, indem Sie die folgenden Optionen aktivieren mithilfe der Spanner-Clientbibliothek.

Java

static void captureGfeMetric(DatabaseClient dbClient) {
  // GFE_latency and other Spanner metrics are automatically collected
  // when OpenTelemetry metrics are enabled.

  try (ResultSet resultSet =
      dbClient
          .singleUse() // Execute a single read or query against Cloud Spanner.
          .executeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"))) {
    while (resultSet.next()) {
      System.out.printf(
          "%d %d %s", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2));
    }
  }
}

Go

// GFE_Latency and other Spanner metrics are automatically collected
// when OpenTelemetry metrics are enabled.
func captureGFELatencyMetric(ctx context.Context, client spanner.Client) error {
	stmt := spanner.Statement{SQL: `SELECT SingerId, AlbumId, AlbumTitle FROM Albums`}
	iter := client.Single().Query(ctx, stmt)
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			return nil
		}
		if err != nil {
			return err
		}
		var singerID, albumID int64
		var albumTitle string
		if err := row.Columns(&singerID, &albumID, &albumTitle); err != nil {
			return err
		}
	}
}

GFE-Latenz visualisieren

Nach dem Abrufen der Messwerte können Sie die GFE-Latenz in Cloud Monitoring

Hier ist ein Beispiel für ein Diagramm, das die Verteilungsaggregation für der GFE-Latenzmesswert. Um die Perzentillatenz bis zum 5. Perzentil zu ändern, Für das 50., 95. oder 99. Perzentil verwenden Sie das Menü Aggregator.

Das Programm erstellt eine Ansicht mit dem Namen spanner/gfe_latency. Dieses wird der String Teil des Namens des Messwerts, wenn er in Cloud Monitoring

GFE-Latenz in Cloud Monitoring

Latenz von Spanner API-Anfragen erfassen und visualisieren

Die Latenz der Spanner API-Anfrage ist die Dauer (in Sekunden) zwischen dem ersten Byte der Anfrage vom Spanner API-Front-End das letzte Byte der Antwort, die die Spanner API erhält, die vom Frontend gesendet werden.

Latenz von Spanner API-Anfragen erfassen

Standardmäßig ist diese Latenz im Rahmen des Cloud Monitoring-Messwerte: Sie müssen nichts unternehmen, um sie zu erfassen und zu exportieren.

Latenz von Spanner API-Anfragen visualisieren

Mit dem Metrics Explorer Diagramm-Tool zum Visualisieren des Diagramms für die Messwert spanner.googleapis.com/api/request_latencies in Cloud Monitoring.

Hier ist ein Beispiel für eine Grafik, die die Latenz des 5. Perzentils für die Latenzmesswert der Spanner API-Anfrage. Um das Perzentil zu ändern zum 50. oder 99. Perzentil haben, verwenden Sie den Aggregator .

Latenz von Cloud Monitoring API-Anfragen

Abfragelatenz erfassen und visualisieren

Die Abfragelatenz ist die Zeit in Millisekunden, die zum Ausführen von SQL benötigt wird Abfragen in der Spanner-Datenbank

Abfragelatenz erfassen

Sie können die Abfragelatenz für die folgenden Sprachen erfassen:

Java

static void captureQueryStatsMetric(OpenTelemetry openTelemetry, DatabaseClient dbClient) {
  // Register query stats metric.
  // This should be done once before start recording the data.
  Meter meter = openTelemetry.getMeter("cloud.google.com/java");
  DoubleHistogram queryStatsMetricLatencies =
      meter
          .histogramBuilder("spanner/query_stats_elapsed")
          .setDescription("The execution of the query")
          .setUnit("ms")
          .build();

  // Capture query stats metric data.
  try (ResultSet resultSet = dbClient.singleUse()
      .analyzeQuery(Statement.of("SELECT SingerId, AlbumId, AlbumTitle FROM Albums"),
          QueryAnalyzeMode.PROFILE)) {

    while (resultSet.next()) {
      System.out.printf(
          "%d %d %s", resultSet.getLong(0), resultSet.getLong(1), resultSet.getString(2));
    }

    String value = resultSet.getStats().getQueryStats()
        .getFieldsOrDefault("elapsed_time", Value.newBuilder().setStringValue("0 msecs").build())
        .getStringValue();
    double elapsedTime = value.contains("msecs")
        ? Double.parseDouble(value.replaceAll(" msecs", ""))
        : Double.parseDouble(value.replaceAll(" secs", "")) * 1000;
    queryStatsMetricLatencies.record(elapsedTime);
  }
}

Go

func captureQueryStatsMetric(ctx context.Context, mp metric.MeterProvider, client spanner.Client) error {
	meter := mp.Meter(spanner.OtInstrumentationScope)
	// Register query stats metric with OpenTelemetry to record the data.
	// This should be done once before start recording the data.
	queryStats, err := meter.Float64Histogram(
		"spanner/query_stats_elapsed",
		metric.WithDescription("The execution of the query"),
		metric.WithUnit("ms"),
		metric.WithExplicitBucketBoundaries(0.0, 0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0,
			16.0, 20.0, 25.0, 30.0, 40.0, 50.0, 65.0, 80.0, 100.0, 130.0, 160.0, 200.0, 250.0,
			300.0, 400.0, 500.0, 650.0, 800.0, 1000.0, 2000.0, 5000.0, 10000.0, 20000.0, 50000.0,
			100000.0),
	)
	if err != nil {
		fmt.Print(err)
	}

	stmt := spanner.Statement{SQL: `SELECT SingerId, AlbumId, AlbumTitle FROM Albums`}
	iter := client.Single().QueryWithStats(ctx, stmt)
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			// Record query execution time with OpenTelemetry.
			elapasedTime := iter.QueryStats["elapsed_time"].(string)
			elapasedTimeMs, err := strconv.ParseFloat(strings.TrimSuffix(elapasedTime, " msecs"), 64)
			if err != nil {
				return err
			}
			queryStats.Record(ctx, elapasedTimeMs)
			return nil
		}
		if err != nil {
			return err
		}
		var singerID, albumID int64
		var albumTitle string
		if err := row.Columns(&singerID, &albumID, &albumTitle); err != nil {
			return err
		}
	}
}

Abfragelatenz visualisieren

Nach dem Abrufen der Messwerte können Sie die Abfragelatenz in Cloud Monitoring

Hier ist ein Beispiel für ein Diagramm, das die Verteilungsaggregation für der GFE-Latenzmesswert. Um die Perzentillatenz bis zum 5. Perzentil zu ändern, Für das 50., 95. oder 99. Perzentil verwenden Sie das Menü Aggregator.

Das Programm erstellt eine OpenCensus-Ansicht mit dem Namen query_stats_elapsed. Dieser String wird beim Exportieren in den Messwertnamen Teil des Messwerts Cloud Monitoring

Cloud Monitoring-Abfragelatenz

Nächste Schritte