Metriche di rete in .NET
le metriche sono misurazioni numeriche registrate nel tempo. Vengono in genere usati per monitorare l'integrità di un'app e generare avvisi.
A partire da .NET 8, i componenti System.Net.Http
e System.Net.NameResolution
vengono strumentati per pubblicare le metriche utilizzando la nuova API System.Diagnostics.Metrics di .NET .
Queste metriche sono state progettate in collaborazione con OpenTelemetry per assicurarsi che siano coerenti con lo standard e funzionino bene con gli strumenti più diffusi come Prometheus e Grafana.
Sono anche multidimensionali, ovvero le misurazioni sono associate a coppie chiave-valore denominate tag (note anche come attributi o etichette). I tag consentono la categorizzazione della misurazione per facilitare l'analisi.
Suggerimento
Per un elenco completo di tutti gli strumenti predefiniti insieme ai relativi attributi, vedere System.Net metriche.
Raccogliere metriche di System.Net
Per sfruttare i vantaggi della strumentazione delle metriche predefinita, è necessario configurare un'app .NET per raccogliere queste metriche. Questo significa in genere trasformarli per l'archiviazione esterna e l'analisi, ad esempio, per i sistemi di monitoraggio.
Esistono diversi modi per raccogliere le metriche di rete in .NET.
- Per una rapida panoramica tramite un semplice esempio autonomo, vedere Raccogliere metriche con dotnet-counters.
- Per raccolta e monitoraggio delle metriche in fase di produzione, è possibile usare Grafana con OpenTelemetry e Prometheus o Azure Monitor Application Insights. Tuttavia, questi strumenti potrebbero risultare scomodi da usare in fase di sviluppo a causa della loro complessità.
- Per raccolta e risoluzione dei problemi tempo di sviluppo, è consigliabile usare .NET Aspirare, che offre un modo semplice ma estendibile per avviare le metriche e la traccia distribuita nell'applicazione e diagnosticare i problemi in locale.
- È anche possibile riutilizzare il progetto Aspire Service Defaults senza l'orchestrazione Aspire, un modo pratico per introdurre le API di configurazione di tracciamento e metriche di OpenTelemetry nel progetto ASP.NET.
Raccogliere metriche con dotnet-counters
dotnet-counters
è uno strumento da riga di comando multipiattaforma per l'esame ad hoc delle metriche .NET e dell'analisi delle prestazioni di primo livello.
Ai fini di questa esercitazione, creare un'app che invii richieste HTTP a vari endpoint in parallelo.
dotnet new console -o HelloBuiltinMetrics
cd ..\HelloBuiltinMetrics
Sostituire il contenuto di Program.cs
con il codice di esempio seguente:
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."); }
});
}
Assicurarsi che dotnet-counters
sia installato:
dotnet tool install --global dotnet-counters
Avviare l'app HelloBuiltinMetrics.
dotnet run -c Release
Avviare dotnet-counters
in una finestra separata dell'interfaccia della riga di comando e specificare il nome del processo e i contatori da controllare, quindi premere un tasto nell'app HelloBuiltinMetrics in modo che inizi a inviare richieste. Non appena le misurazioni iniziano l'atterraggio, dotnet-counters
aggiorna continuamente la console con i numeri più recenti:
dotnet-counters monitor --counters System.Net.Http,System.Net.NameResolution -n HelloBuiltinMetrics
Raccogliere metriche con .NET Aspire
Un modo semplice per raccogliere tracce e metriche nelle applicazioni ASP.NET consiste nell'usare .NET Aspire. .NET Aspire è un set di estensioni per .NET per semplificare la creazione e l'uso di applicazioni distribuite. Uno dei vantaggi dell'uso di .NET Aspire è che i dati di telemetria sono incorporati, usando le librerie OpenTelemetry per .NET.
I modelli di progetto predefiniti per .NET Aspire contengono un progetto ServiceDefaults
. Ogni servizio nella soluzione .NET Aspire ha un riferimento al progetto Service Defaults. I servizi lo usano per impostare e configurare OTel.
Il modello di progetto Service Defaults include i pacchetti OTel SDK, ASP.NET, HttpClient e Strumentazione di esecuzione. Questi componenti di strumentazione vengono configurati nel file Extensions.cs. Per supportare la visualizzazione dei dati di telemetria in Aspira dashboard, il progetto Service Defaults include anche l'utilità di esportazione OTLP per impostazione predefinita.
Aspira dashboard è progettato per portare l'osservazione dei dati di telemetria al ciclo di debug locale, che consente agli sviluppatori di garantire che le applicazioni producano dati di telemetria. La visualizzazione dei dati di telemetria consente anche di diagnosticare tali applicazioni in locale. La possibilità di osservare le chiamate tra i servizi è utile in fase di debug, come nell'ambiente di produzione. Il Dashboard .NET Aspire viene avviato automaticamente quando si F5 il progetto AppHost
da Visual Studio o dotnet run
il progetto AppHost
dalla riga di comando.
Procedura dettagliata rapida
Crea un'app .NET Aspire 9 Starter utilizzando
dotnet new
:dotnet new aspire-starter-9 --output AspireDemo
In alternativa, in Visual Studio, creare un nuovo progetto e selezionare il modello app iniziale .NET Aspire 9:
Aprire
Extensions.cs
nel progetto diServiceDefaults
e scorrere fino al metodoConfigureOpenTelemetry
. Si noti la chiamataAddHttpClientInstrumentation()
che abbonata ai contatori di rete..WithMetrics(metrics => { metrics.AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddRuntimeInstrumentation(); })
Nota che in .NET 8+,
AddHttpClientInstrumentation()
può essere sostituito da iscrizioni manuali al misuratore..WithMetrics(metrics => { metrics.AddAspNetCoreInstrumentation() .AddMeter("System.Net.Http") .AddMeter("System.Net.NameResolution") .AddRuntimeInstrumentation(); })
Eseguire il progetto
AppHost
. Verrà avviata la dashboard Aspire.Navigare alla pagina Meteo dell'app
webfrontend
per generare una richiesta diHttpClient
versoapiservice
. Aggiornare la pagina più volte per inviare più richieste.Torna al Dashboard , passa alla pagina delle metriche e seleziona la risorsa
webfrontend
. Scorrendo verso il basso, dovresti riuscire a esplorare le metriche predefiniteSystem.Net
.
Per altre informazioni su .NET Aspire, vedere:
- Panoramica Aspire
- telemetria di in Aspira
- Aspire Dashboard
Riutilizzare il progetto Service Defaults senza orchestrazione .NET Aspire
Il progetto Aspira servizio predefinito offre un modo semplice per configurare OTel per i progetti ASP.NET, anche se non si usano il resto di .NET Aspire come AppHost per l'orchestrazione. Il progetto Service Defaults è disponibile come modello di progetto tramite Visual Studio o dotnet new
. Configura OTel e prepara l'esportatore OTLP. È quindi possibile usare le variabili di ambiente OTel per configurare l'endpoint OTLP per inviare dati di telemetria e fornire le proprietà delle risorse per l'applicazione.
I passaggi per usare ServiceDefaults all'esterno di .NET Aspire sono:
Aggiungere il progetto ServiceDefaults alla soluzione usando Aggiungi nuovo progetto in Visual Studio oppure usare
dotnet new
:dotnet new aspire-servicedefaults --output ServiceDefaults
Fai riferimento al progetto ServiceDefaults dalla tua applicazione ASP.NET. In Visual Studio, selezionare Aggiungi>Riferimento al Progetto e selezionare il progetto ServiceDefaults
Chiamare la funzione di installazione di OpenTelemetry
ConfigureOpenTelemetry()
come parte dell'inizializzazione del generatore di applicazioni.var builder = WebApplication.CreateBuilder(args) builder.ConfigureOpenTelemetry(); // Extension method from ServiceDefaults. var app = builder.Build(); app.MapGet("/", () => "Hello World!"); app.Run();
Per una guida completa, vedere Esempio: Usare OpenTelemetry con OTLP e Aspire Dashboard autonomo.
Visualizzare le metriche in Grafana con OpenTelemetry e Prometheus
Per informazioni su come connettere un'app di esempio con Prometheus e Grafana, seguire la procedura dettagliata descritta in Uso di OpenTelemetry con Prometheus, Grafana e Jaeger.
Per stressare HttpClient
inviando richieste parallele a vari endpoint, estendere l'app di esempio con l'endpoint seguente:
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.";
});
Creare un dashboard di Grafana selezionando l'icona + sulla barra degli strumenti superiore e quindi selezionando Dashboard. Nell'editor del dashboard visualizzato immettere Connessioni HTTP/1.1 Aperte nella casella Titolo e la seguente query nel campo espressione PromQL:
sum by(http_connection_state) (http_client_open_connections{network_protocol_version="1.1"})
Selezionare Applica per salvare e visualizzare il nuovo dashboard. Visualizza il numero di connessioni HTTP/1.1 attive e inattive nel pool.
Arricchimento
L'arricchimento è l'aggiunta di tag personalizzati (noti anche come attributi o etichette) a metriche. Ciò è utile se un'app vuole aggiungere una categorizzazione personalizzata ai dashboard o agli avvisi compilati con le metriche.
Lo strumento http.client.request.duration
supporta l'arricchimento registrando i callback con il HttpMetricsEnrichmentContext.
Si noti che si tratta di un'API di basso livello e per ogni HttpRequestMessage
è necessaria una registrazione di callback separata.
Un modo semplice per eseguire la registrazione del callback in un'unica posizione consiste nell'implementare un DelegatingHandlerpersonalizzato. In questo modo è possibile intercettare e modificare le richieste prima che vengano inoltrate al gestore interno e inviate al server:
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);
}
}
Se si usa IHttpClientFactory
, è possibile usare AddHttpMessageHandler per registrare il EnrichmentHandler
:
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");
Nota
Per motivi di prestazioni, il callback di arricchimento viene richiamato solo quando lo strumento http.client.request.duration
è abilitato, vale a dire che qualcosa deve raccogliere le metriche.
Può essere dotnet-monitor
, un esportatore Prometheus, un MeterListener
o un MetricCollector<T>
.
integrazione di IMeterFactory
e IHttpClientFactory
Le metriche HTTP sono state progettate tenendo conto dell'isolamento e della testabilità. Questi aspetti sono supportati dall'uso di IMeterFactory, che consente la pubblicazione di metriche da un'istanza di Meter personalizzata per mantenere i contatori isolati l'uno dall'altro.
Per impostazione predefinita, viene utilizzato un Meter globale per emettere tutte le metriche. Questa Meter è interna alla libreria System.Net.Http
. Questo comportamento può essere modificato assegnando un'istanza personalizzata di IMeterFactory a SocketsHttpHandler.MeterFactory o HttpClientHandler.MeterFactory.
Nota
Il Meter.Name è System.Net.Http
per tutte le metriche generate da HttpClientHandler
e SocketsHttpHandler
.
Quando si lavora con Microsoft.Extensions.Http
e IHttpClientFactory
in .NET 8+, l'implementazione di IHttpClientFactory
predefinita seleziona automaticamente l'istanza IMeterFactory
registrata nel IServiceCollection e la assegna al gestore primario che crea internamente.
Nota
A partire da .NET 8, il metodo AddHttpClient chiama automaticamente AddMetrics per inizializzare i servizi delle metriche e registrare l'implementazione IMeterFactory predefinita con IServiceCollection. Il IMeterFactory predefinito memorizza nella cache Meter istanze in base al nome, ovvero è presente un Meter con il nome System.Net.Http
per IServiceCollection.
Metriche di test
L'esempio seguente illustra come convalidare le metriche predefinite negli unit test usando xUnit, IHttpClientFactory
e MetricCollector<T>
dal pacchetto NuGet Microsoft.Extensions.Diagnostics.Testing
:
[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"]);
});
}
Metriche e EventCounters
Le metriche sono più ricche di funzionalità rispetto a EventCounters, soprattutto per via della loro natura multidimensionale. Questa multidimensionalità consente di creare query sofisticate in strumenti come Prometheus e ottenere informazioni dettagliate su un livello non possibile con EventCounters.
Tuttavia, a partire da .NET 8, solo i componenti System.Net.Http
e System.Net.NameResolutions
vengono instrumentati usando le metriche, ovvero se sono necessari contatori dai livelli inferiori dello stack, ad esempio System.Net.Sockets
o System.Net.Security
, è necessario usare EventCounters.
Esistono inoltre alcune differenze semantiche tra le metriche e i relativi EventCounters corrispondenti.
Ad esempio, quando si usa HttpCompletionOption.ResponseContentRead
, l'current-requests
EventCounter considera attiva una richiesta fino al momento in cui è stato letto l'ultimo byte del corpo della richiesta.
La controparte delle metriche http.client.active_requests
non include il tempo impiegato per leggere il corpo della risposta durante il conteggio delle richieste attive.
Sono necessarie altre metriche?
Se avete suggerimenti per altre informazioni utili che potrebbero essere espresse tramite le metriche, creare una segnalazione dotnet/runtime.