Examiner la latence dans un composant Spanner avec OpenTelemetry

Cet article explique comment examiner un composant Spanner pour trouver la source de la latence et la visualiser à l'aide d'OpenTelemetry. Pour une présentation générale des composants de cette rubrique, consultez Points de latence d'une requête Spanner

OpenTelemetry est un kit et un framework d'observabilité Open Source qui permet vous créez et gérez des données de télémétrie telles que des traces, des métriques et des journaux. Il est le résultat d'une fusion entre OpenTracing et OpenCensus. Pour plus d'informations, consultez la page Qu'est-ce qu'OpenTelemetry ?

Les bibliothèques clientes Spanner fournissent des métriques et des traces avec du framework d'observabilité OpenTelemetry. Suivez la procédure décrite dans Identifiez le point de latence pour trouver la qui affichent une latence dans Spanner.

Avant de commencer

Avant de commencer à collecter des métriques de latence, familiarisez-vous avec la avec OpenTelemetry. Vous devez configurer le SDK OpenTelemetry. avec les options appropriées pour exporter vos données de télémétrie. Il existe plusieurs Options d'exportateur OpenTelemetry disponibles Nous vous recommandons d'utiliser OpenTelemetry Exportateur de protocole (OTLP, Protocol). Vous pouvez également utiliser un collecteur Otel. avec Google Cloud Exporter ou Google Managed Service pour Prometheus Exporter.

Si vous exécutez votre application sur Compute Engine, vous pouvez utiliser l'agent Agent pour collecter les métriques et les traces du protocole OpenTelemetry. Pour plus d'informations, consultez Collecter des métriques et des traces OTLP.

Ajouter des dépendances

Pour configurer le SDK OpenTelemetry et l'exportateur OTLP, ajoutez les éléments suivants : les dépendances à votre application.

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

Injecter l'objet OpenTelemetry

Ensuite, créez un objet OpenTelemetry avec l'exportateur OTLP et injectez le Objet OpenTelemetry à l'aide de 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"),
		))
}

Capturer et visualiser la latence aller-retour du client

La latence aller-retour du client correspond à la durée (en millisecondes) entre les premier octet de la requête API Spanner que le client envoie la base de données (via le GFE et l'interface de l'API Spanner), et le dernier octet de réponse que le client reçoit de la base de données.

Capturer la latence aller-retour du client

La métrique de latence aller-retour du client Spanner n'est pas compatible à l'aide d'OpenTelemetry. Vous pouvez instrumenter la métrique à l'aide d'OpenCensus avec un pont et migrer les données vers OpenTelemetry.

Visualiser la latence aller-retour du client

Après avoir récupéré les métriques, vous pouvez visualiser la latence aller-retour du client dans Cloud Monitoring.

Voici un exemple de graphique illustrant la latence du 5e centile pour le de latence aller-retour du client. Pour modifier la latence en centiles, vous devez : (50e ou 99e centiles), utilisez le menu Agrégateur.

Le programme crée une vue appelée roundtrip_latency. Cette chaîne fait partie du nom de la métrique lorsqu'elle est exportée vers Cloud Monitoring.

Latence aller-retour du client Cloud Monitoring.

Capturer et visualiser la latence GFE

La latence de Google Front End (GFE) correspond à la durée (en millisecondes) entre lorsque le réseau Google reçoit un appel de procédure à distance du client et lorsque le GFE reçoit le premier octet de la réponse.

Capturer la latence du GFE

Vous pouvez capturer les métriques de latence du GFE en activant les options suivantes à l'aide de la bibliothèque cliente Spanner.

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
		}
	}
}

Visualiser la latence GFE

Après avoir récupéré les métriques, vous pouvez visualiser la latence du GFE dans Cloud Monitoring.

Voici un exemple de graphique illustrant l'agrégation de distribution pour la métrique de latence du GFE. Pour définir la latence en centiles sur le 5e, Pour les 50e, 95e ou 99e centiles, utilisez le menu Agrégateur.

Le programme crée une vue appelée spanner/gfe_latency. Ce devient une partie du nom de la métrique lorsqu'elle est exportée vers Cloud Monitoring.

Latence de GFE Cloud Monitoring.

Capturer et visualiser la latence des requêtes API Spanner

La latence des requêtes API Spanner correspond à la durée (en secondes) entre le premier octet de la requête que l'interface de l'API Spanner et le dernier octet de réponse reçu par l'API Spanner frontend envoie.

Capturer la latence des requêtes API Spanner

Par défaut, cette latence est disponible Métriques Cloud Monitoring. Vous n'avez rien à faire pour la capturer et l'exporter.

Visualiser la latence des requêtes de l'API Spanner

Vous pouvez utiliser l'Explorateur de métriques. pour visualiser le graphique spanner.googleapis.com/api/request_latencies dans Cloud Monitoring.

Voici un exemple de graphique illustrant la latence du 5e centile pour le Métrique de latence des requêtes de l'API Spanner. Pour modifier le centile au 50e ou au 99e centile, utilisez l'outil Aggregator (Agrégateur). .

Latence des requêtes API Cloud Monitoring.

Capturer et visualiser la latence des requêtes

La latence d'une requête est la durée (en millisecondes) nécessaire pour exécuter SQL dans la base de données Spanner.

Capturer la latence des requêtes

Vous pouvez capturer la latence des requêtes pour les langages suivants :

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
		}
	}
}

Visualiser la latence des requêtes

Après avoir récupéré les métriques, vous pouvez visualiser la latence des requêtes dans Cloud Monitoring.

Voici un exemple de graphique illustrant l'agrégation de distribution pour la métrique de latence du GFE. Pour définir la latence en centiles sur le 5e, Pour les 50e, 95e ou 99e centiles, utilisez le menu Agrégateur.

Le programme crée une vue OpenCensus appelée query_stats_elapsed. Cette chaîne est incorporée au nom de la métrique lorsqu'elle est exportée vers Cloud Monitoring.

Latence des requêtes Cloud Monitoring.

Étape suivante