Delen via


Metrische netwerkgegevens in .NET

Metrische gegevens zijn numerieke metingen die in de loop van de tijd worden gerapporteerd. Ze worden meestal gebruikt om de status van een app te bewaken en waarschuwingen te genereren.

Vanaf .NET 8 worden de System.Net.Http en de System.Net.NameResolution onderdelen geïnstrueerd om metrische gegevens te publiceren met behulp van . De nieuwe SYSTEM.Diagnostics.Metrics-API van NET. Deze metrische gegevens zijn ontworpen in samenwerking met OpenTelemetry om ervoor te zorgen dat ze consistent zijn met de standaard en goed werken met populaire hulpprogramma's zoals Prometheus en Grafana. Ze zijn ook multidimensionaal, wat betekent dat metingen zijn gekoppeld aan sleutel-waardeparen die tags (a.k.a. kenmerken of labels) worden genoemd waarmee gegevens kunnen worden gecategoriseerd voor analyse.

Metrische gegevens voor System.Net verzamelen

Er zijn twee onderdelen voor het gebruik van metrische gegevens in een .NET-app:

  • Instrumentatie: Code in .NET-bibliotheken neemt metingen en koppelt deze metingen aan een metrische naam. .NET en ASP.NET Core bevatten veel ingebouwde metrische gegevens.
  • Verzameling: Een .NET-app configureert benoemde metrische gegevens die moeten worden verzonden vanuit de app voor externe opslag en analyse. Sommige hulpprogramma's kunnen configuraties uitvoeren buiten de app met behulp van configuratiebestanden of een ui-hulpprogramma.

In deze sectie ziet u verschillende methoden voor het verzamelen en weergeven van System.Net metrische gegevens.

Voorbeeld-app

Maak voor deze zelfstudie een eenvoudige app waarmee HTTP-aanvragen parallel naar verschillende eindpunten worden verzonden.

dotnet new console -o HelloBuiltinMetrics
cd ..\HelloBuiltinMetrics

Vervang de inhoud door Program.cs de volgende voorbeeldcode:

using System.Net;

string[] uris = ["http://example.com", "http://httpbin.org/get", "https://example.com", "https://httpbin.org/get"];
using HttpClient client = new()
{
    DefaultRequestVersion = HttpVersion.Version20
};

Console.WriteLine("Press any key to start.");
Console.ReadKey();

while (!Console.KeyAvailable)
{
    await Parallel.ForAsync(0, Random.Shared.Next(20), async (_, ct) =>
    {
        string uri = uris[Random.Shared.Next(uris.Length)];
        byte[] bytes = await client.GetByteArrayAsync(uri, ct);
        await Console.Out.WriteLineAsync($"{uri} - received {bytes.Length} bytes.");
    });
}

Metrische gegevens weergeven met dotnet-tellers

dotnet-counters is een platformoverschrijdend hulpprogramma voor prestatiebewaking voor ad-hocstatusbewaking en prestatieonderzoek op het eerste niveau.

dotnet tool install --global dotnet-counters

Bij uitvoering op basis van een .NET 8+-proces worden dotnet-counters de instrumenten ingeschakeld die door het --counters argument zijn gedefinieerd en worden de metingen weergegeven. De console wordt continu vernieuwd met de nieuwste nummers:

dotnet-counters monitor --counters System.Net.Http,System.Net.NameResolution -n HelloBuiltinMetrics

Metrische gegevens weergeven in Grafana met OpenTelemetry en Prometheus

Overzicht

OpenTelemetry:

  • Is een leverancierneutraal opensource-project dat wordt ondersteund door de Cloud Native Computing Foundation.
  • Standaardiseert het genereren en verzamelen van telemetrie voor cloudeigen software.
  • Werkt met .NET met behulp van de metrische .NET-API's.
  • Wordt goedgekeurd door Azure Monitor en veel APM-leveranciers.

In deze zelfstudie ziet u een van de integraties die beschikbaar zijn voor metrische gegevens van OpenTelemetry met behulp van de OSS Prometheus - en Grafana-projecten . De gegevensstroom met metrische gegevens bestaat uit de volgende stappen:

  1. De metrische .NET-API's registreren metingen uit de voorbeeld-app.

  2. De OpenTelemetry-bibliotheek die in de app wordt uitgevoerd, voegt de metingen samen.

  3. De Prometheus-exportbibliotheek maakt de geaggregeerde gegevens beschikbaar via een eindpunt voor metrische HTTP-gegevens. 'Exporteur' is wat OpenTelemetry de bibliotheken aanroept die telemetrie verzenden naar leverancierspecifieke back-ends.

  4. Een Prometheus-server:

    • Peilt het eindpunt voor metrische gegevens.
    • Leest de gegevens.
    • Slaat de gegevens op in een database voor persistentie op lange termijn. Prometheus verwijst naar het lezen en opslaan van gegevens als het scrapen van een eindpunt.
    • Kan worden uitgevoerd op een andere computer.
  5. De Grafana-server:

    • Query's uitvoeren op de gegevens die zijn opgeslagen in Prometheus en deze weergeven op een bewakingsdashboard op internet.
    • Kan worden uitgevoerd op een andere computer.

De voorbeeld-app configureren voor het gebruik van de Prometheus-exporteur van OpenTelemetry

Voeg een verwijzing naar de OpenTelemetry Prometheus-exporteur toe aan de voorbeeld-app:

dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener --prerelease

Notitie

In deze zelfstudie wordt gebruikgemaakt van een voorlopige build van de Prometheus-ondersteuning van OpenTelemetry die beschikbaar is op het moment van schrijven.

Bijwerken Program.cs met OpenTelemetry-configuratie:

using OpenTelemetry.Metrics;
using OpenTelemetry;
using System.Net;

using MeterProvider meterProvider = Sdk.CreateMeterProviderBuilder()
    .AddMeter("System.Net.Http", "System.Net.NameResolution")
    .AddPrometheusHttpListener(options => options.UriPrefixes = new string[] { "http://localhost:9184/" })
    .Build();

string[] uris = ["http://example.com", "http://httpbin.org/get", "https://example.com", "https://httpbin.org/get"];
using HttpClient client = new()
{
    DefaultRequestVersion = HttpVersion.Version20
};

while (!Console.KeyAvailable)
{
    await Parallel.ForAsync(0, Random.Shared.Next(20), async (_, ct) =>
    {
        string uri = uris[Random.Shared.Next(uris.Length)];
        byte[] bytes = await client.GetByteArrayAsync(uri, ct);
        await Console.Out.WriteLineAsync($"{uri} - received {bytes.Length} bytes.");
    });
}

In de voorgaande code:

  • AddMeter("System.Net.Http", "System.Net.NameResolution") configureert OpenTelemetry om alle metrische gegevens te verzenden die zijn verzameld door de ingebouwde System.Net.Http en System.Net.NameResolution meters.
  • AddPrometheusHttpListener configureert OpenTelemetry om het HTTP-eindpunt voor metrische gegevens van Prometheus beschikbaar te maken op poort 9184.

Notitie

Deze configuratie verschilt voor ASP.NET Core-apps, waarbij metrische gegevens worden geëxporteerd OpenTelemetry.Exporter.Prometheus.AspNetCore in plaats van HttpListener. Zie het gerelateerde ASP.NET Core-voorbeeld.

Voer de app uit en laat deze actief zodat metingen kunnen worden verzameld:

dotnet run

Prometheus instellen en configureren

Volg de eerste stappen van Prometheus om een Prometheus-server in te stellen en te bevestigen dat deze werkt.

Wijzig het prometheus.yml configuratiebestand zodat Prometheus het eindpunt voor metrische gegevens verwijdert dat de voorbeeld-app weergeeft. Voeg de volgende gemarkeerde tekst toe in de scrape_configs sectie:

# my global config
global:
  scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
    - static_configs:
        - targets:
          # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: "prometheus"

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
      - targets: ["localhost:9090"]

  - job_name: 'OpenTelemetryTest'
    scrape_interval: 1s # poll very quickly for a more responsive demo
    static_configs:
      - targets: ['localhost:9184']

Prometheus starten

  1. Laad de configuratie opnieuw of start de Prometheus-server opnieuw op.

  2. Controleer of OpenTelemetryTest de UP-status heeft op de pagina Statusdoelen> van de Prometheus-webportal. Prometheus status

  3. Voer http op de graph-pagina van de Prometheus-webportal het tekstvak voor expressies in en selecteer http_client_active_requests. http_client_active_requests Op het grafiektabblad toont Prometheus de waarde van de http.client.active_requests teller die wordt verzonden door de voorbeeld-app. Prometheus active requests graph

Metrische gegevens weergeven op een Grafana-dashboard

  1. Volg de standaardinstructies om Grafana te installeren en deze te verbinden met een Prometheus-gegevensbron.

  2. Maak een Grafana-dashboard door het + pictogram op de bovenste werkbalk te selecteren en vervolgens Dashboard te selecteren. Voer in de dashboardeditor die wordt weergegeven Http/1.1 open Verbinding maken ions in het vak Titel en de volgende query in het veld PromQL-expressie in:

sum by(http_connection_state) (http_client_open_connections{network_protocol_version="1.1"})

Grafana HTTP/1.1 Connections

  1. Selecteer Toepassen om het nieuwe dashboard op te slaan en weer te geven. Hiermee wordt het aantal actieve en niet-actieve HTTP/1.1-verbindingen in de pool weergegeven.

Enrichment

Verrijking is de toevoeging van aangepaste tags (a.k.a. kenmerken of labels) aan een metrische waarde. Dit is handig als een app een aangepaste categorisatie wil toevoegen aan dashboards of waarschuwingen die zijn gebouwd met metrische gegevens. Het http.client.request.duration instrument ondersteunt verrijking door callbacks te registreren bij de HttpMetricsEnrichmentContext. Houd er rekening mee dat dit een API op laag niveau is en dat er voor elke HttpRequestMessageapi een afzonderlijke callback-registratie nodig is.

Een eenvoudige manier om de callback-registratie op één plaats uit te voeren, is door een aangepaste DelegatingHandlerimplementatie uit te voeren. Hiermee kunt u de aanvragen onderscheppen en wijzigen voordat ze worden doorgestuurd naar de binnenhandler en naar de server worden verzonden:

using System.Net.Http.Metrics;

using HttpClient client = new(new EnrichmentHandler() { InnerHandler = new HttpClientHandler() });

await client.GetStringAsync("https://httpbin.org/response-headers?Enrichment-Value=A");
await client.GetStringAsync("https://httpbin.org/response-headers?Enrichment-Value=B");

sealed class EnrichmentHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpMetricsEnrichmentContext.AddCallback(request, static context =>
        {
            if (context.Response is not null) // Response is null when an exception occurs.
            {
                // Use any information available on the request or the response to emit custom tags.
                string? value = context.Response.Headers.GetValues("Enrichment-Value").FirstOrDefault();
                if (value != null)
                {
                    context.AddCustomTag("enrichment_value", value);
                }
            }
        });
        return base.SendAsync(request, cancellationToken);
    }
}

Als u werkt, IHttpClientFactorykunt u het volgende gebruiken AddHttpMessageHandler om het EnrichmentHandlervolgende te registreren:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System.Net.Http.Metrics;

ServiceCollection services = new();
services.AddHttpClient(Options.DefaultName).AddHttpMessageHandler(() => new EnrichmentHandler());

ServiceProvider serviceProvider = services.BuildServiceProvider();
HttpClient client = serviceProvider.GetRequiredService<HttpClient>();

await client.GetStringAsync("https://httpbin.org/response-headers?Enrichment-Value=A");
await client.GetStringAsync("https://httpbin.org/response-headers?Enrichment-Value=B");

Notitie

Om prestatieredenen wordt de callback voor verrijking alleen aangeroepen wanneer het http.client.request.duration instrument is ingeschakeld, wat betekent dat er iets moet zijn om de metrische gegevens te verzamelen. Dit kan zijn dotnet-monitor, Prometheus-exporteur, een MeterListenerof een MetricCollector<T>.

IMeterFactory en IHttpClientFactory integratie

HTTP-metrische gegevens zijn ontworpen met isolatie en testbaarheid in gedachten. Deze aspecten worden ondersteund door het gebruik van IMeterFactory, waardoor metrische gegevens kunnen worden gepubliceerd door een aangepast exemplaar Meter om meters gescheiden van elkaar te houden. Standaard worden alle metrische gegevens verzonden door een globaal Meter intern naar de System.Net.Http bibliotheek. Dit gedrag kan worden overschreven door een aangepast exemplaar IMeterFactory toe te wijzen aan SocketsHttpHandler.MeterFactory of HttpClientHandler.MeterFactory.

Notitie

Dit Meter.Name geldt System.Net.Http voor alle metrische gegevens die worden verzonden door HttpClientHandler en SocketsHttpHandler.

Wanneer u met Microsoft.Extensions.Http en IHttpClientFactory op .NET 8+ werkt, kiest de standaard IHttpClientFactory implementatie automatisch het IMeterFactory exemplaar dat in de IServiceCollection instantie is geregistreerd en wijst deze toe aan de primaire handler die intern wordt gemaakt.

Notitie

Vanaf .NET 8 wordt de AddHttpClient methode automatisch aangeroepen AddMetrics om de metrische services te initialiseren en de standaard IMeterFactory implementatie bij IServiceCollectionte registreren. De exemplaren van de standaardcaches IMeterFactoryMeter op naam, wat betekent dat er één Meter met de naam System.Net.Http per IServiceCollection.

Metrische gegevens testen

In het volgende voorbeeld ziet u hoe u ingebouwde metrische gegevens in eenheidstests valideert met behulp van xUnit, IHttpClientFactoryen MetricCollector<T> vanuit het Microsoft.Extensions.Diagnostics.Testing NuGet-pakket:

[Fact]
public async Task RequestDurationTest()
{
    // Arrange
    ServiceCollection services = new();
    services.AddHttpClient();
    ServiceProvider serviceProvider = services.BuildServiceProvider();
    var meterFactory = serviceProvider.GetService<IMeterFactory>();
    var collector = new MetricCollector<double>(meterFactory,
        "System.Net.Http", "http.client.request.duration");
    var client = serviceProvider.GetRequiredService<HttpClient>();

    // Act
    await client.GetStringAsync("http://example.com");

    // Assert
    await collector.WaitForMeasurementsAsync(minCount: 1).WaitAsync(TimeSpan.FromSeconds(5));
    Assert.Collection(collector.GetMeasurementSnapshot(),
        measurement =>
        {
            Assert.Equal("http", measurement.Tags["url.scheme"]);
            Assert.Equal("GET", measurement.Tags["http.request.method"]);
        });
}

Metrische gegevens versus EventCounters

Metrische gegevens zijn uitgebreider dan EventCounters, met name vanwege hun multidimensionale aard. Met deze multidimensionale functionaliteit kunt u geavanceerde query's maken in hulpprogramma's zoals Prometheus en inzichten verkrijgen op een niveau dat niet mogelijk is met EventCounters.

Vanaf .NET 8 worden echter alleen de System.Net.Http onderdelen System.Net.NameResolutions geïnstrueerd met behulp van metrische gegevens, wat betekent dat als u tellers van de lagere niveaus van de stack nodig hebt, zoals System.Net.Sockets of System.Net.Security, u EventCounters moet gebruiken.

Bovendien zijn er enkele semantische verschillen tussen metrische gegevens en hun overeenkomende EventCounters. Wanneer de EventCounter bijvoorbeeld een HttpCompletionOption.ResponseContentReadcurrent-requests aanvraag beschouwt om actief te zijn tot het moment waarop de laatste byte van de aanvraagbody is gelezen. De tegenhanger http.client.active_requests voor metrische gegevens bevat niet de tijd die is besteed aan het lezen van de hoofdtekst van het antwoord bij het tellen van de actieve aanvragen.

Meer metrische gegevens nodig?

Als u suggesties hebt voor andere nuttige informatie die kan worden weergegeven via metrische gegevens, maakt u een probleem met dotnet/runtime.