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
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
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:
Öffnen Sie
Extensions.cs
imServiceDefaults
Projekt, und scrollen Sie zurConfigureOpenTelemetry
Methode. Beachten Sie denAddHttpClientInstrumentation()
-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(); })
Führen Sie das
AppHost
Projekt aus. Dadurch sollte das Aspire Dashboard gestartet werden.Navigieren Sie zur Seite Wetter der
webfrontend
App, um eineHttpClient
Anfrage in Richtungapiservice
zu generieren. Aktualisieren Sie die Seite mehrmals, um mehrere Anfragen zu senden.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 integriertenSystem.Net
Metriken zu durchsuchen.
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 new
verfü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:
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
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"
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.
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 HttpRequestMessage
eine 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 IHttpClientFactory
arbeiten, können Sie AddHttpMessageHandler verwenden, um die EnrichmentHandler
zu 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 MeterListener
oder 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, IHttpClientFactory
und 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.Security
benö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.ResponseContentRead
verwenden, 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.