Freigeben über


Netzwerkmetriken in .NET

Metriken sind numerische Werte, die im Laufe der Zeit erfasst werden. Sie werden in der Regel verwendet, um den Status einer App zu überwachen und Warnungen zu generieren.

Ab .NET 8 werden die System.Net.Http und System.Net.NameResolution Komponenten instrumentiert, um Metriken mithilfe der neuen .NET System.Diagnostics.Metrics APIzu veröffentlichen. Diese Metriken wurden in Zusammenarbeit mit OpenTelemetry entwickelt, um sicherzustellen, dass sie mit dem Standard konsistent sind und gut mit beliebten Tools wie Prometheus und Grafanafunktionieren. Sie sind auch mehrdimensional, was bedeutet, dass Messwerte Schlüssel-Wert-Paaren zugeordnet sind, die als Tags bezeichnet werden (auch als Attribute oder Beschriftungen bezeichnet). Tags ermöglichen die Kategorisierung der Messung, um die Analyse zu unterstützen.

Tipp

Eine umfassende Liste aller integrierten Instrumente zusammen mit ihren Attributen finden Sie unter System.Net Metriken.

Sammeln System.Net Metriken

Um die integrierte Metrikinstrumentation nutzen zu können, muss eine .NET-App so konfiguriert werden, dass diese Metriken erfasst werden. Dies bedeutet in der Regel, sie für externe Speicher und Analysen zu transformieren, z. B. in Überwachungssysteme.

Es gibt mehrere Möglichkeiten zum Sammeln von Netzwerkmetriken in .NET.

  • Eine kurze Übersicht mithilfe eines einfachen, eigenständigen Beispiels finden Sie unter Sammeln von Metriken mit dotnet-counters.
  • Für die Sammlung und Überwachung von Metriken zur Produktionszeit können Sie Grafana mit OpenTelemetry und Prometheus oder Azure Monitor Application Insights verwenden. Diese Tools können jedoch aufgrund ihrer Komplexität bei der Entwicklung unpraktisch sein.
  • Für das Sammeln von Metriken zur Entwicklungszeit und die Fehlerbehebung empfehlen wir .NET Aspire, das eine einfache, aber erweiterbare Möglichkeit bietet, Metriken und verteiltes Tracing in Ihrer Anwendung zu starten und Probleme lokal zu diagnostizieren.
  • Es ist auch möglich, das Aspire Service Defaults Projekt ohne die Aspire Orchestrierung wiederzuverwenden. Dies ist eine praktische Möglichkeit, die OpenTelemetry Tracing- und Metrik-Konfigurations-APIs in Ihr ASP.NET Projekt einzuführen.

Sammeln von Metriken mit Dotnet-Zählern

dotnet-counters ist ein plattformübergreifendes Befehlszeilentool zur Ad-hoc-Untersuchung von .NET-Metriken und Leistungsuntersuchungen auf erster Ebene.

Erstellen Sie für dieses Lernprogramm eine App, die HTTP-Anforderungen parallel an verschiedene Endpunkte sendet.

dotnet new console -o HelloBuiltinMetrics
cd ..\HelloBuiltinMetrics

Ersetzen Sie den Inhalt von Program.cs durch den folgenden Beispielcode:

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)];
        try
        {
            byte[] bytes = await client.GetByteArrayAsync(uri, ct);
            await Console.Out.WriteLineAsync($"{uri} - received {bytes.Length} bytes.");
        }
        catch { await Console.Out.WriteLineAsync($"{uri} - failed."); }
    });
}

Stellen Sie sicher, dass dotnet-counters installiert ist:

dotnet tool install --global dotnet-counters

Starten Sie die HelloBuiltinMetrics-App.

dotnet run -c Release

Starten Sie dotnet-counters in einem separaten CLI-Fenster, und geben Sie den Prozessnamen und die zu überwachenden Meter an, und drücken Sie dann in der HelloBuiltinMetrics-App eine Taste, damit das Senden von Anforderungen gestartet wird. Sobald die Messungen eintreffen, aktualisiert dotnet-counters kontinuierlich die Konsole mit den neuesten Werten.

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

dotnet-counters Ausgabe

Sammeln von Metriken mit .NET Aspire

Eine einfache Möglichkeit zum Sammeln von Traces und Metriken in ASP.NET-Anwendungen ist die Verwendung von .NET Aspire. .NET Aspire ist eine Reihe von Erweiterungen für .NET, um das Erstellen und Arbeiten mit verteilten Anwendungen zu vereinfachen. Einer der Vorteile der Verwendung von .NET Aspire besteht darin, dass Telemetrie integriert ist, indem die OpenTelemetry-Bibliotheken für .NET verwendet werden.

Die Standardprojektvorlagen für .NET Aspire enthalten ein ServiceDefaults Projekt. Jeder Dienst in der .NET Aspire-Lösung weist einen Verweis auf das Projekt "Service Defaults" auf. Die Dienste verwenden sie zum Einrichten und Konfigurieren von OTel.

Die Projektvorlage "Dienststandard" enthält die OTel-SDK-, ASP.NET-, HttpClient- und Runtime Instrumentation-Pakete. Diese Instrumentierungskomponenten sind in der datei Extensions.cs konfiguriert. Zur Unterstützung der Telemetrievisualisierung im Aspire Dashboard enthält das Dienststandardprojekt standardmäßig auch den OTLP-Exporter.

Das Aspire Dashboard wurde entwickelt, um Telemetriebeobachtungen in den lokalen Debugzyklus zu bringen, wodurch Entwickler sicherstellen können, dass die Anwendungen Telemetrie erzeugen. Die Telemetrievisualisierung hilft auch, diese Anwendungen lokal zu diagnostizieren. Die Möglichkeit, die Aufrufe zwischen Diensten zu beobachten, ist zum Debugzeitpunkt so nützlich wie in der Produktion. Das Dashboard von .NET Aspire wird automatisch gestartet, wenn Sie F5 das AppHost-Projekt von Visual Studio oder dotnet run das AppHost-Projekt von der Kommandozeile aus aufrufen.

Exemplarische Vorgehensweise

  1. Erstellen Sie eine .NET Aspire 9 Starter App mithilfe von dotnet new:

    dotnet new aspire-starter-9 --output AspireDemo
    

    Oder erstellen Sie in Visual Studio ein neues Projekt, und wählen Sie die Vorlage .NET Aspire 9 Starter App aus:

    Erstellen einer .NET Aspire 9 Starter-App in Visual Studio

  2. Öffnen Sie Extensions.cs im ServiceDefaults Projekt, und scrollen Sie zur ConfigureOpenTelemetry Methode. Beachten Sie den AddHttpClientInstrumentation()-Aufruf, der die Networking-Meter abonniert.

    .WithMetrics(metrics =>
    {
        metrics.AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddRuntimeInstrumentation();
    })
    

    Beachten Sie, dass auf .NET 8+ AddHttpClientInstrumentation() durch manuelle Meterabonnements ersetzt werden können:

    .WithMetrics(metrics =>
    {
        metrics.AddAspNetCoreInstrumentation()
            .AddMeter("System.Net.Http")
            .AddMeter("System.Net.NameResolution")
            .AddRuntimeInstrumentation();
    })
    
  3. Führen Sie das AppHost Projekt aus. Dadurch sollte das Aspire Dashboard gestartet werden.

  4. Navigieren Sie zur Seite Wetter der webfrontend App, um eine HttpClient Anfrage in Richtung apiservice zu generieren. Aktualisieren Sie die Seite mehrmals, um mehrere Anfragen zu senden.

  5. Kehren Sie zum Dashboard zurück, navigieren Sie zur Seite Metriken, und wählen Sie die webfrontend Ressource aus. Wenn Sie nach unten scrollen, sollten Sie in der Lage sein, die integrierten System.Net Metriken zu durchsuchen.

    Netzwerkmetriken im Aspire Dashboard

Weitere Informationen zu .NET Aspire finden Sie unter:

Wiederverwendung des Aspire Service Defaults Projekts ohne .NET Aspire Orchestrierung

Das Projekt Aspire Service Defaults bietet eine einfache Möglichkeit, OTel für ASP.NET-Projekte zu konfigurieren, auch wenn Sie den Rest von .NET Aspire wie z. B. den AppHost für die Orchestrierung nicht verwenden. Das Dienststandardprojekt ist als Projektvorlage über Visual Studio oder dotnet newverfügbar. Er konfiguriert OTel und richtet den OTLP-Exporter ein. Anschließend können Sie die OTel-Umgebungsvariablen verwenden, um den OTLP-Endpunkt zum Senden von Telemetrie zu konfigurieren und die Ressourceneigenschaften für die Anwendung bereitzustellen.

Die Schritte zur Verwendung ServiceDefaults außerhalb von .NET Aspire sind:

  1. Fügen Sie das Projekt ServiceDefaults mithilfe von "Neues Projekt hinzufügen" in Visual Studio zur Projektmappe hinzu, oder verwenden Sie dotnet new:

    dotnet new aspire-servicedefaults --output ServiceDefaults
    
  2. Verweisen Sie auf das ServiceDefaults- Projekt aus Ihrer ASP.NET Anwendung. Wählen Sie in Visual Studio Hinzufügen>Projektreferenz und wählen Sie das ServiceDefaults-Projekt"

  3. Rufen Sie die OpenTelemetry-Setupfunktion ConfigureOpenTelemetry() als Teil der Initialisierung des Anwendungs-Generators auf.

    var builder = WebApplication.CreateBuilder(args)
    builder.ConfigureOpenTelemetry(); // Extension method from ServiceDefaults.
    var app = builder.Build();
    app.MapGet("/", () => "Hello World!");
    app.Run();
    

Eine vollständige exemplarische Vorgehensweise finden Sie unter Beispiel: Verwenden von OpenTelemetry mit OTLP und dem eigenständigen Aspire Dashboard.

Anzeigen von Metriken in Grafana mit OpenTelemetry und Prometheus

Um zu sehen, wie Sie eine Beispiel-App mit Prometheus und Grafana verbinden, befolgen Sie die exemplarische Vorgehensweise in Verwenden von OpenTelemetry mit Prometheus, Grafana und Jaeger.

Um HttpClient zu betonen, indem parallele Anforderungen an verschiedene Endpunkte gesendet werden, erweitern Sie die Beispiel-App mit dem folgenden Endpunkt:

app.MapGet("/ClientStress", async Task<string> (ILogger<Program> logger, HttpClient client) =>
{
    string[] uris = ["http://example.com", "http://httpbin.org/get", "https://example.com", "https://httpbin.org/get"];
    await Parallel.ForAsync(0, 50, async (_, ct) =>
    {
        string uri = uris[Random.Shared.Next(uris.Length)];

        try
        {
            await client.GetAsync(uri, ct);
            logger.LogInformation($"{uri} - done.");
        }
        catch { logger.LogInformation($"{uri} - failed."); }
    });
    return "Sent 50 requests to example.com and httpbin.org.";
});

Erstellen Sie ein Grafana-Dashboard, indem Sie auf der oberen Symbolleiste das symbol + und dann Dashboard-auswählen. Geben Sie im daraufhin angezeigten Dashboard-Editor Open HTTP/1.1 Connections in das Feld Titel und die folgende Abfrage im Feld „PromQL-Ausdruck“ ein:

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

Klicken Sie auf Anwenden, um das neue Dashboard zu speichern und anzuzeigen. Es zeigt die Anzahl aktiver und leerer HTTP/1.1-Verbindungen im Pool an.

HTTP/1.1-Verbindungen in Grafana

Anreicherung

Anreicherung ist das Hinzufügen von angepassten Tags (auch bekannt als Attribute oder Kennzeichnungen) zu einer Metrik. Dies ist nützlich, wenn eine App eine benutzerdefinierte Kategorisierung zu Dashboards oder Warnungen hinzufügen möchte, die mit Metriken erstellt wurden. Das http.client.request.duration-Instrument unterstützt die Anreicherung durch Registrieren von Rückrufen bei der HttpMetricsEnrichmentContext. Beachten Sie, dass dies eine API mit niedriger Ebene ist und für jede HttpRequestMessageeine separate Rückrufregistrierung erforderlich ist.

Eine einfache Möglichkeit, die Rückrufregistrierung an einem zentralen Ort durchzuführen, besteht darin, eine benutzerdefinierte DelegatingHandlerzu implementieren. Auf diese Weise können Sie die Anforderungen abfangen und ändern, bevor sie an den inneren Handler weitergeleitet und an den Server gesendet werden:

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

Wenn Sie mit IHttpClientFactoryarbeiten, können Sie AddHttpMessageHandler verwenden, um die EnrichmentHandlerzu registrieren:

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

Anmerkung

Aus Leistungsgründen wird der Anreicherungsrückruf nur aufgerufen, wenn das http.client.request.duration-Instrument aktiviert ist, was bedeutet, dass etwas die Metriken erfasst. Dies kann dotnet-monitor, Prometheus-Exporter, ein MeterListeneroder ein MetricCollector<T> sein.

IMeterFactory und IHttpClientFactory Integration

HTTP-Metriken wurden mit Isolation und Testbarkeit entworfen. Diese Aspekte werden durch die Verwendung von IMeterFactoryunterstützt, die es ermöglicht, Metriken über eine benutzerdefinierte Meter Instanz zu veröffentlichen, um die Meter voneinander isoliert zu halten. Standardmäßig wird ein globales Meter verwendet, um alle Metriken auszugeben. Dies Meter intern in der System.Net.Http-Bibliothek. Dieses Verhalten kann durch Zuweisung einer angepassten IMeterFactory-Instanz an SocketsHttpHandler.MeterFactory oder HttpClientHandler.MeterFactory außer Kraft gesetzt werden.

Anmerkung

Meter.Name ist System.Net.Http für alle Metriken, die von HttpClientHandler und SocketsHttpHandler ausgegeben werden.

Wenn Sie mit Microsoft.Extensions.Http und IHttpClientFactory auf .NET 8+ arbeiten, wählt die standardimplementierung IHttpClientFactory automatisch die im IServiceCollection registrierte IMeterFactory-Instanz aus und weist sie dem primären Handler zu, den sie intern erstellt.

Anmerkung

Ab .NET 8 ruft die AddHttpClient-Methode automatisch AddMetrics auf, um die Metrikdienste zu initialisieren und die Standardimplementierung IMeterFactory bei IServiceCollectionzu registrieren. Die Voreinstellung IMeterFactory zwischenspeichert Meter-Instanzen nach Namen, was bedeutet, dass es pro IServiceCollection eine Meter-Instanz mit dem Namen System.Net.Http gibt.

Testmetriken

Im folgenden Beispiel wird veranschaulicht, wie integrierte Metriken in Komponententests mithilfe von xUnit, IHttpClientFactoryund MetricCollector<T> aus dem Microsoft.Extensions.Diagnostics.Testing NuGet-Paket überprüft werden:

[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"]);
        });
}

Metriken im Vergleich zu EventCounters

Metriken sind funktionsreicher als die EventCounters, vor allem wegen ihrer mehrdimensionalen Natur. Mit dieser mehrdimensionalen Eigenschaft können Sie komplexe Abfragen in Tools wie Prometheus erstellen und Einblicke auf eine Ebene erhalten, die mit EventCounters nicht möglich ist.

Ab .NET 8 werden jedoch nur die System.Net.Http und die System.Net.NameResolutions Komponenten mithilfe von Metriken instrumentiert, was bedeutet, dass Sie bei Bedarf Zähler aus den unteren Ebenen des Stapels wie System.Net.Sockets oder System.Net.Securitybenötigen, müssen Sie EventCounters verwenden.

Darüber hinaus gibt es einige semantische Unterschiede zwischen Metriken und deren übereinstimmenden EventCounters. Wenn Sie z. B. HttpCompletionOption.ResponseContentReadverwenden, betrachtet das current-requests EventCounter eine Aktive Anforderung bis zum Zeitpunkt, in dem das letzte Byte des Anforderungstexts gelesen wurde. Die Kennzahl http.client.active_requests enthält nicht die Zeit, die für das Lesen des Antwortinhalts aufgewendet wird, wenn die aktiven Anfragen gezählt werden.

Benötigen Sie weitere Metriken?

Wenn Sie Vorschläge für andere nützliche Informationen haben, die über Metriken verfügbar gemacht werden könnten, erstellen Sie einen Issue in dotnet/runtime.