Esaminare la latenza in un componente Spanner con OpenTelemetry

Questo argomento descrive come esaminare un componente Spanner per trovare l'origine della latenza e visualizzarne la latenza utilizzando OpenTelemetry. Per una panoramica generale dei componenti di questo argomento, consulta Punti di latenza in una richiesta Spanner.

OpenTelemetry è un framework e un toolkit di osservabilità open source che consente puoi creare e gestire dati di telemetria come tracce, metriche e log. È il risultato della fusione tra OpenTraccia e OpenCensus. Per ulteriori informazioni, consulta la sezione Che cos'è OpenTelemetry?

Le librerie client di Spanner forniscono metriche e tracce con l'uso del framework di osservabilità OpenTelemetry. Segui la procedura in Identifica il punto di latenza per trovare componenti o componenti che mostrano latenza in Spanner.

Prima di iniziare

Prima di iniziare ad acquisire le metriche di latenza, familiarizza con le strumentazione con OpenTelemetry. Devi configurare l'SDK OpenTelemetry e le opzioni appropriate per esportare i dati di telemetria. Esistono diversi tipi di Opzioni di esportazione OpenTelemetry disponibili. Ti consigliamo di usare lo strumento OpenTelemetry Esportatore di protocolli (OTLP). Altre opzioni includono l'utilizzo di un raccoglitore OTel con Google Cloud Exporter o Google Managed Service per Prometheus Exporter.

Se esegui la tua applicazione su Compute Engine, puoi utilizzare Agente per raccogliere metriche e tracce del protocollo OpenTelemetry. Per ulteriori informazioni, consulta Raccolta di metriche e tracce OTLP.

Aggiungi dipendenze

Per configurare l'SDK OpenTelemetry e l'esportatore OTLP, aggiungi quanto segue le dipendenze per la tua applicazione.

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>

Vai

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

Inserisci l'oggetto OpenTelemetry

Quindi, crea un oggetto OpenTelemetry con l'esportatore OTLP e inserisci il valore Oggetto OpenTelemetry utilizzando 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();

Vai

// 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"),
		))
}

Acquisisci e visualizza la latenza di round trip del client

La latenza di round trip del client è il periodo di tempo (in millisecondi) che intercorre tra primo byte della richiesta API Spanner che il client invia a il database (tramite il frontend GFE e l'API Spanner), e l'ultimo byte di risposta che il client riceve dal database.

Acquisisci la latenza di round trip del client

La metrica di latenza di round trip del client Spanner non è supportata utilizzando OpenTelemetry. Puoi instrumentare la metrica utilizzando OpenCensus con una ponte ed eseguire la migrazione dei dati in OpenTelemetry.

Visualizza la latenza di round trip del client

Dopo aver recuperato le metriche, puoi visualizzare la latenza di andata e ritorno del client in e configurazione in Cloud Monitoring.

Ecco un esempio di grafico che illustra la latenza del 5° percentile per la latenza di andata e ritorno del client. Per modificare la latenza percentile in il 50° o il 99° percentile, utilizza il menu Aggregatore.

Il programma crea una vista denominata roundtrip_latency. Questa stringa diventa parte del nome della metrica quando viene esportata in Cloud Monitoring.

latenza di round trip del client Cloud Monitoring.

Acquisizione e visualizzazione della latenza GFE

La latenza di Google Front End (GFE) è il periodo di tempo (in millisecondi) che intercorre quando la rete Google riceve una chiamata di procedura remota dal client e quando il GFE riceve il primo byte della risposta.

Acquisizione della latenza GFE

Puoi acquisire le metriche di latenza dei GFE abilitando le seguenti opzioni usando la libreria client di 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));
    }
  }
}

Vai

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

Visualizzare la latenza dei GFE

Dopo aver recuperato le metriche, puoi visualizzare la latenza dei GFE e configurazione in Cloud Monitoring.

Ecco un esempio di grafico che illustra l'aggregazione della distribuzione per la metrica di latenza GFE. Per cambiare la latenza percentile al 5°, 50°, 95° o 99° percentile, utilizza il menu Aggregatore.

Il programma crea una vista denominata spanner/gfe_latency. Questo diventa parte del nome della metrica quando viene esportata in e configurazione in Cloud Monitoring.

Latenza GFE di Cloud Monitoring.

Acquisisci e visualizza la latenza delle richieste API Spanner

La latenza delle richieste API Spanner è il periodo di tempo (in secondi) tra il primo byte di richiesta che il frontend dell'API Spanner riceve l'ultimo byte di risposta che l'API Spanner gli invii frontend.

Acquisizione della latenza delle richieste API Spanner

Per impostazione predefinita, questa latenza è disponibile come parte Metriche di Cloud Monitoring. Non devi fare nulla per acquisirla ed esportarla.

Visualizza la latenza delle richieste API Spanner

Puoi utilizzare Esplora metriche. per visualizzare il grafico spanner.googleapis.com/api/request_latencies in Cloud Monitoring.

Ecco un esempio di grafico che illustra la latenza del 5° percentile per Metrica di latenza delle richieste API Spanner. Per modificare il percentile di latenza al 50° o 99° percentile, utilizza l'Aggregatore o dal menu Fogli Google.

Latenza delle richieste API Cloud Monitoring.

Acquisisci e visualizza la latenza delle query

La latenza delle query è il tempo (in millisecondi) necessario per eseguire SQL nel database Spanner.

Acquisisci latenza delle query

Puoi acquisire la latenza delle query nelle seguenti lingue:

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

Vai

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

Visualizza la latenza delle query

Dopo aver recuperato le metriche, puoi visualizzare la latenza delle query e configurazione in Cloud Monitoring.

Ecco un esempio di grafico che illustra l'aggregazione della distribuzione per la metrica di latenza GFE. Per cambiare la latenza percentile al 5°, 50°, 95° o 99° percentile, utilizza il menu Aggregatore.

Il programma crea una vista OpenCensus chiamata query_stats_elapsed. Questa stringa diventa parte del nome della metrica quando viene esportata in e configurazione in Cloud Monitoring.

Latenza delle query di Cloud Monitoring.

Passaggi successivi