OpenCensus-Messwerte verwenden

Auf dieser Seite werden die Grundlagen zum Erstellen von OpenCensus-Messwerten für Verfügbarkeits- und Latenz-SLIs erläutert. Außerdem finden Sie Implementierungsbeispiele zum Definieren von SLOs mithilfe von OpenCensus-Messwerten.

Die Grundlagen von OpenCensus

OpenCensus ist eine einzelne Open-Source-Distribution von Bibliotheken, die auf der OpenCensus GitHub-Seite verfügbar sind und automatisch Traces und Messwerte erfassen und zu jedem Back-End senden. OpenCensus kann zur Instrumentierung Ihrer Dienste verwendet werden, 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: Zeichnet einen bestimmten Datenpunkt auf, der von einem Measure für ein bestimmtes Ereignis erfasst und geschrieben wurde. Beispielsweise kann ein Measurement die Latenz einer bestimmten Antwort aufzeichnen.
  • 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 erfasste Wert.

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

Instrumentierung

Die OpenCensus-Bibliotheken sind für verschiedene Sprachen verfügbar. Sprachspezifische Informationen zur Instrumentierung Ihres Dienstes zur Ausgabe von Messwerten finden Sie unter Sprachunterstützung in OpenCensus. Darüber hinaus finden Sie unter Benutzerdefinierte Messwerte mit OpenCensus Beispiele für Sprachen, die häufig für das Monitoring verwendet werden.

Im Standardfall gehen Sie so vor:

  • Instrumentieren Sie Ihren Dienst für die Aufzeichnung und den Export von Messwerten.
  • Definieren Sie einen Exporter, der die Messwerte empfängt.

Für jeden Messwert müssen Sie einen Measure definieren, um den Werttyp anzugeben: Int64 oder Float64. Außerdem müssen Sie die View definieren und registrieren, um den Aggregationstyp (Anzahl, Verteilung, Summe oder letzter Wert) anzugeben. Wenn Sie den Verteilungsaggregationstyp verwenden möchten, müssen Sie die Grenzen des Histogramm-Buckets auch explizit angeben. Außerdem geben Sie in der View einen Namen für den Messwert an.

Exporter

Schließlich müssen Sie einen Exporter verwenden, um die Messwerte zu erfassen und in Cloud Monitoring oder 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ählungsaggregation.
  • Verwenden Sie für Latenz-SLIs einen Measure mit Verteilungsaggregation.

Messwerte für Verfügbarkeits-SLIs

Sie geben einen anfragebasierten Verfügbarkeits-SLI in der Cloud Monitoring API an, indem Sie mit der Struktur TimeSeriesRatio ein Verhältnis von „guten” oder „schlechten” Anfragen zu den gesamten Anfragen festlegen. Dieses Verhältnis wird im Feld goodTotalRatio einer RequestBasedSli-Struktur verwendet.

Ihre Anwendung muss OpenCensus-Messwerte erstellen, mit denen dieses Verhältnis erstellt werden kann. In Ihrer Anwendung müssen Sie 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 geben einen anfragebasierten Latenz-SLI in der Cloud Monitoring API mithilfe 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 erstellen, indem Sie den Verteilungsaggregationstyp verwenden. Sie müssen die Bucket-Grenzen auch explizit definieren. Beachten Sie, dass es wichtig ist, die Buckets so zu definieren, dass Sie den Prozentsatz der Anfragen, die innerhalb des gewünschten Schwellenwerts liegen, genau messen können. Eine Erläuterung dieses Themas finden Sie unter SLOs implementieren im Site Reliability Engineering Workbook.

Implementierungsbeispiel

In diesem Abschnitt wird ein Beispiel vorgestellt, in dem Messwerte für grundlegende Verfügbarkeits- und Latenz-SLIs mithilfe von 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 einen 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. Aufzeichnen von Werten für die Messwerte „Anfrageanzahl” und „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. So wird beispielsweise der Name jedes OpenCensus-View in der Node.js-Implementierung so 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"
}