OpenCensus-Messwerte verwenden

Auf dieser Seite werden die Grundlagen zum Erstellen von OpenCensus-Messwerten für die Verfügbarkeits- und Latenz-SLIs behandelt. Außerdem finden Sie Implementierungsbeispiele dafür, wie SLOs mit von OpenCensus-Messwerten definiert werden.

Grundlagen von OpenCensus

OpenCensus ist eine Open-Source-Distribution von Bibliotheken, die über die OpenCensus GitHub-Seite verfügbar ist. Sie erfasst Traces und Messwerte automatisch und sendet diese zu einem beliebigen Back-End. Mit OpenCensus können Sie Ihre Dienste instrumentieren, um benutzerdefinierte Messwerte auszugeben, die in Cloud Monitoring aufgenommen werden können. Sie können diese Messwerte dann als SLIs verwenden.

Ein Beispiel dafür, wie Sie mit OpenCensus Monitoring-Messwerte erstellen, die nicht explizit für SLIs gedacht sind, finden Sie unter Benutzerdefinierte Messwerte mit OpenCensus.

Messwerte

Um mit OpenCensus Messwertdaten für Ihren Dienst zu erfassen, müssen Sie folgende OpenCensus-Konstrukte verwenden:

  • Measure stellt den zu erfassenden Messwerttyp dar, angegeben durch einen Messwertnamen. Ein Measure kann Werte vom Typ Int64 oder Float64 aufzeichnen.
  • Measurement: Erfasst einen bestimmten Datenpunkt, der von einem Measure für ein bestimmtes Ereignis erfasst und geschrieben wird. Beispiel: Mit Measurement kann die Latenz einer bestimmten Antwort erfasst werden.
  • View gibt eine Aggregation an, die auf Measure angewendet wurde. OpenCensus unterstützt folgende Aggregationstypen:
    • Anzahl: Die Anzahl der Messpunkte.
    • Verteilung: Eine Histogrammverteilung der Messpunkte.
    • Summe: Eine Summe an Messwerten.
    • LastValue: Der letzte von der Messung aufgezeichnete Wert.

Weitere Informationen finden Sie unter OpenCensus-Statistiken/-Messwerte. In OpenCensus werden Messwerte oft als stats bezeichnet.

Instrumentierung

Es stehen OpenCensus-Bibliotheken für verschiedene Sprachen zur Verfügung. Sprachenspezifische Informationen zur Instrumentierung Ihres Dienstes für das Ausgeben von Messwerten finden Sie unter OpenCensus-Sprachenunterstützung. Unter Benutzerdefinierte Messwerte mit OpenCensus finden Sie Beispiele für Sprachen, die häufig mit Monitoring verwendet werden.

Im Standardfall gehen Sie so vor:

  • Instrumentieren Sie Ihren Dienst, um Messwerte aufzuzeichnen und zu exportieren.
  • Definieren Sie einen Exporter, der die Messwerte empfängt.

Für jeden Messwert müssen Sie einen Measure definieren, um den Werttyp ("Int64" oder "Float64") anzugeben. Sie müssen auch View definieren und registrieren, um den Zusammenfassungstyp (Anzahl, Verteilung, Summe oder letzter Wert) anzugeben. Zur Nutzung des Verteilungsaggregationstyps müssen Sie auch die Grenzen des Histogramm-Buckets explizit festlegen. Außerdem geben Sie im View einen Namen für den Messwert an.

Exporter

Schließlich benötigen Sie einen Exporter, um die Messwerte zu erfassen und in Cloud Monitoring oder in ein anderes Back-End zu schreiben. Informationen zu den für Monitoring verfügbaren sprachspezifischen Exportern finden Sie unter OpenCensus-Exporter.

Sie können auch einen eigenen Exporter schreiben. Weitere Informationen finden Sie unter Benutzerdefinierten Exporter schreiben.

Messwerte für SLIs erstellen

Ihre Anwendung muss OpenCensus-Messwerte erstellen, die als SLIs in Cloud Monitoring verwendet werden können:

  • Verwenden Sie für Verfügbarkeits-SLIs für Anfrage- und Fehlerzahlen einen Measure mit Zählaggregation.
  • Verwenden Sie für Latenz-SLIs eine Measure mit Verteilungsaggregation.

Messwerte für Verfügbarkeits-SLIs

Um anfragebasierte Verfügbarkeits-SLIs in der Cloud Monitoring API zu definieren, verwenden Sie die Struktur TimeSeriesRatio und richten ein Verhältnis von "guten" oder "fehlerhaften" Anfragen zu den insgesamten Anfragen ein. Dieses Verhältnis wird im Feld goodTotalRatio einer RequestBasedSli-Struktur verwendet.

Ihre Anwendung muss OpenCensus-Messwerte erstellen, mit denen dieses Verhältnis konstruiert werden kann. Sie müssen in Ihrer Anwendung mindestens zwei der folgenden Elemente erstellen:

  1. Ein Messwert, mit dem alle Ereignisse gezählt werden. Verwenden Sie diesen Messwert im totalServiceFilter des Verhältnisses.

    Sie können einen OpenCensus-Messwert des Typs Int64 mit Zahlaggregation erstellen und für jede empfangene Anfrage den Wert 1 erfassen.

  2. Verwenden Sie diesen Messwert im badServiceFilter des Verhältnisses, um "schlechte" Ereignisse zu zählen.

    Sie können einen OpenCensus-Messwert des Typs Int64 mit Zahlaggregation erstellen und für jeden Fehler oder jede fehlgeschlagene Anfrage den Wert 1 erfassen.

  3. Verwenden Sie diesen Messwert im goodServiceFilter des Verhältnisses, um "gute" Ereignisse zu zählen.

    Sie können einen OpenCensus-Messwert des Typs Int64 mit Zahlaggregation erstellen und für jede erfolgreiche Antwort den Wert 1 erfassen.

Messwerte für Latenz-SLIs

Sie definieren anfragebasierte Latenz-SLIs in der Cloud Monitoring API mit einer DistributionCut-Struktur aus. Diese Struktur wird im Feld distributionCut einer RequestBasedSli-Struktur verwendet.

Sie können ein Int64- oder Float64-Measure mit einem View mit dem Typ der Verteilungsaggregation erstellen. Sie müssen auch die Bucket-Grenzen explizit definieren. Beachten Sie, dass es wichtig ist, die Buckets so zu definieren, dass Sie den Anteil der innerhalb des gewünschten Grenzwerts liegenden Anfragen genau messen können. Eine Beschreibung dieses Themas finden Sie im Abschnitt SLOs implementieren im Handbuch zur Site Reliability Engineering.

Implementierungsbeispiel

In diesem Abschnitt wird ein Beispiel vorgestellt, in dem Messwerte für grundlegende Verfügbarkeits- und Latenz-SLIs mit OpenCensus in Node.js implementiert werden.

Instrumentierung

So instrumentieren Sie Ihren Dienst für die Ausgabe von Messwerten mit OpenCensus:

  1. Fügen Sie die erforderlichen Bibliotheken hinzu:

    Go

    import (
    	"flag"
    	"fmt"
    	"log"
    	"math/rand"
    	"net/http"
    	"time"
    
    	"contrib.go.opencensus.io/exporter/stackdriver"
    	"go.opencensus.io/stats"
    	"go.opencensus.io/stats/view"
    	"go.opencensus.io/tag"
    )
    

    Node.js

    // opencensus setup
    const {globalStats, MeasureUnit, AggregationType} = require('@opencensus/core');
    const {StackdriverStatsExporter} = require('@opencensus/exporter-stackdriver');

    Python

    from flask import Flask
    from opencensus.ext.prometheus import stats_exporter as prometheus
    from opencensus.stats import aggregation as aggregation_module
    from opencensus.stats import measure as measure_module
    from opencensus.stats import stats as stats_module
    from opencensus.stats import view as view_module
    from opencensus.tags import tag_map as tag_map_module
    
    from prometheus_flask_exporter import PrometheusMetrics
    
  2. Definieren und registrieren Sie den Exporter:

    Go

    // Sets up Cloud Monitoring exporter.
    sd, err := stackdriver.NewExporter(stackdriver.Options{
    	ProjectID:         *projectID,
    	MetricPrefix:      "opencensus-demo",
    	ReportingInterval: 60 * time.Second,
    })
    if err != nil {
    	log.Fatalf("Failed to create the Cloud Monitoring exporter: %v", err)
    }
    defer sd.Flush()
    
    sd.StartMetricsExporter()
    defer sd.StopMetricsExporter()

    Node.js

    // Stackdriver export interval is 60 seconds
    const EXPORT_INTERVAL = 60;
    const exporter = new StackdriverStatsExporter({
      projectId: projectId,
      period: EXPORT_INTERVAL * 1000,
    });
    globalStats.registerExporter(exporter);

    Python

    def setup_openCensus_and_prometheus_exporter() -> None:
        stats = stats_module.stats
        view_manager = stats.view_manager
        exporter = prometheus.new_stats_exporter(prometheus.Options(namespace="oc_python"))
        view_manager.register_exporter(exporter)
        register_all_views(view_manager)
  3. Definieren Sie für jeden Messwert eine Measure:

    Go

    // Sets up metrics.
    var (
    	requestCount       = stats.Int64("oc_request_count", "total request count", "requests")
    	failedRequestCount = stats.Int64("oc_failed_request_count", "count of failed requests", "requests")
    	responseLatency    = stats.Float64("oc_latency_distribution", "distribution of response latencies", "s")
    )
    

    Node.js

    const REQUEST_COUNT = globalStats.createMeasureInt64(
      'request_count',
      MeasureUnit.UNIT,
      'Number of requests to the server'
    );
    const ERROR_COUNT = globalStats.createMeasureInt64(
      'error_count',
      MeasureUnit.UNIT,
      'Number of failed requests to the server'
    );
    const RESPONSE_LATENCY = globalStats.createMeasureInt64(
      'response_latency',
      MeasureUnit.MS,
      'The server response latency in milliseconds'
    );

    Python

    m_request_count = measure_module.MeasureInt(
        "python_request_count", "total requests", "requests"
    )
    m_failed_request_count = measure_module.MeasureInt(
        "python_failed_request_count", "failed requests", "requests"
    )
    m_response_latency = measure_module.MeasureFloat(
        "python_response_latency", "response latency", "s"
    )
  4. Definieren und registrieren Sie die View für jede Measure mit dem entsprechenden Zusammenfassungstyp sowie die Antwortlatenz für die Bucket-Grenzwerte:

    Go

    // Sets up views.
    var (
    	requestCountView = &view.View{
    		Name:        "oc_request_count",
    		Measure:     requestCount,
    		Description: "total request count",
    		Aggregation: view.Count(),
    	}
    	failedRequestCountView = &view.View{
    		Name:        "oc_failed_request_count",
    		Measure:     failedRequestCount,
    		Description: "count of failed requests",
    		Aggregation: view.Count(),
    	}
    	responseLatencyView = &view.View{
    		Name:        "oc_response_latency",
    		Measure:     responseLatency,
    		Description: "The distribution of the latencies",
    		// Bucket definitions must be explicitly specified.
    		Aggregation: view.Distribution(0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000),
    	}
    )
    
    	// Register the views.
    	if err := view.Register(requestCountView, failedRequestCountView, responseLatencyView); err != nil {
    		log.Fatalf("Failed to register the views: %v", err)
    	}

    Node.js

    const request_count_metric = globalStats.createView(
      'request_count_metric',
      REQUEST_COUNT,
      AggregationType.COUNT
    );
    globalStats.registerView(request_count_metric);
    const error_count_metric = globalStats.createView(
      'error_count_metric',
      ERROR_COUNT,
      AggregationType.COUNT
    );
    globalStats.registerView(error_count_metric);
    const latency_metric = globalStats.createView(
      'response_latency_metric',
      RESPONSE_LATENCY,
      AggregationType.DISTRIBUTION,
      [],
      'Server response latency distribution',
      // Latency in buckets:
      [0, 1000, 2000, 3000, 4000, 5000, 10000]
    );
    globalStats.registerView(latency_metric);

    Python

    # set up views
    latency_view = view_module.View(
        "python_response_latency",
        "The distribution of the latencies",
        [],
        m_response_latency,
        aggregation_module.DistributionAggregation(
            [0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]
        ),
    )
    
    request_count_view = view_module.View(
        "python_request_count",
        "total requests",
        [],
        m_request_count,
        aggregation_module.CountAggregation(),
    )
    
    failed_request_count_view = view_module.View(
        "python_failed_request_count",
        "failed requests",
        [],
        m_failed_request_count,
        aggregation_module.CountAggregation(),
    )
    
    # register views
    def register_all_views(view_manager: stats_module.stats.view_manager) -> None:
        view_manager.register_view(latency_view)
        view_manager.register_view(request_count_view)
        view_manager.register_view(failed_request_count_view)
  5. Erfassen Sie Werte für die Anzahl der Anfragen und die Fehleranzahl:

    Go

    // Counts the request.
    stats.Record(ctx, requestCount.M(1))
    
    // Randomly fails 10% of the time.
    if rand.Intn(100) >= 90 {
    	// Counts the error.
    	stats.Record(ctx, failedRequestCount.M(1))

    Node.js

    // record a request count for every request
    globalStats.record([
      {
        measure: REQUEST_COUNT,
        value: 1,
      },
    ]);
    
    // randomly throw an error 10% of the time
    const randomValue = Math.floor(Math.random() * 9 + 1);
    if (randomValue === 1) {
      // Record a failed request.
      globalStats.record([
        {
          measure: ERROR_COUNT,
          value: 1,
        },
      ]);

    Python

    mmap = stats_recorder.new_measurement_map()
    # count request
    mmap.measure_int_put(m_request_count, 1)
    # fail 10% of the time
    if random.randint(0, 100) > 90:
        mmap.measure_int_put(m_failed_request_count, 1)
        tmap = tag_map_module.TagMap()
        mmap.record(tmap)
        return ("error!", 500)
  6. Latenzwerte aufzeichnen:

    Go

    requestReceived := time.Now()
    // Records latency for failure OR success.
    defer func() {
    	stats.Record(ctx, responseLatency.M(time.Since(requestReceived).Seconds()))
    }()

    Node.js

    globalStats.record([
      {
        measure: RESPONSE_LATENCY,
        value: stopwatch.elapsedMilliseconds,
      },
    ]);

    Python

    start_time = time.perf_counter()
    mmap = stats_recorder.new_measurement_map()
    if random.randint(0, 100) > 90:
        response_latency = time.perf_counter() - start_time
        mmap.measure_float_put(m_response_latency, response_latency)
        tmap = tag_map_module.TagMap()
        mmap.record(tmap)

Aufgenommene Messwerte

Wenn Ihre Messwerte nach Cloud Monitoring exportiert werden, werden sie als Messwerttypen mit einem Präfix angezeigt, das angibt, dass sie von OpenCensus stammen. Beispiel: Der Name jeder OpenCensus-View in der Node.js-Implementierung wird folgendermaßen zugeordnet:

  • request_count_sli wird zu custom.googleapis.com/opencensus/request_count_sli.
  • error_count_sli wird zu custom.googleapis.com/opencensus/error_count_sli.
  • response_latency_sli wird zu custom.googleapis.com/opencensus/response_latency_sli.

Sobald Ihr Dienst ausgeführt wird, können Sie prüfen, ob die Messwerte in Monitoring aufgenommen werden. Dazu suchen Sie diese im Metrics Explorer.

Verfügbarkeits-SLIs

In Cloud Monitoring geben Sie anfragebasierte Verfügbarkeits-SLIs mit einer TimeSeriesRatio-Struktur an. Das folgende Beispiel zeigt ein SLO, das die aufgenommenen OpenCensus-Messwerte verwendet und erwartet, dass der Dienst eine Verfügbarkeit von 98% erzielt. Der Wert ergibt sich aus dem Verhältnis von error_count_sli zu request_count_sli über ein rollierendes 28-Tage-Fenster:

{
  "serviceLevelIndicator": {
    "requestBased": {
      "goodTotalRatio": {
        "totalServiceFilter":
          "metric.type=\"custom.googleapis.com/opencensus/request_count_sli\",
       "badServiceFilter":
          "metric.type=\"custom.googleapis.com/opencensus/error_count_sli\"
      }
    }
  },
  "goal": 0.98,
  "rollingPeriod": "2419200s",
  "displayName": "98% Availability, rolling 28 days"
}

Latenz-SLIs

In Cloud Monitoring geben Sie anfragebasierte Latenz-SLIs mit einer DistributionCut-Struktur an. Folgendes Beispiel zeigt ein SLO, das den aufgenommenen OpenCensus-Latenzmesswert verwendet und erwartet, dass 98 % der Anfragen in weniger als 1.000 ms über ein rollierendes Zeitfenster von einem Tag abgeschlossen wurden:

{
  "serviceLevelIndicator": {
    "requestBased": {
      "distributionCut": {
        "distributionFilter":
          "metric.type=\"custom.googleapis.com/opencensus/response_latency_sli\",
        "range": {
          "min": 0,
          "max": 1000
        }
      }
    }
  },
  "goal": 0.98,
  "rollingPeriod": "86400s",
  "displayName": "98% requests under 1000 ms"
}