Métricas de rede no .NET
As métricas são medições numéricas relatadas ao longo do tempo. Eles geralmente são usados para monitorar a integridade de um aplicativo e gerar alertas.
A partir do .NET 8, os System.Net.Http
componentes e são System.Net.NameResolution
instrumentados para publicar métricas usando o . A nova API System.Diagnostics.Metrics da NET.
Essas métricas foram projetadas em cooperação com o OpenTelemetry para garantir que sejam consistentes com o padrão e funcionem bem com ferramentas populares como Prometheus e Grafana.
Eles também são multidimensionais, o que significa que as medições estão associadas a pares chave-valor chamados tags (também conhecidos como atributos ou rótulos) que permitem que os dados sejam categorizados para análise.
Gorjeta
Para obter uma lista abrangente de todos os instrumentos internos, juntamente com seus atributos, consulte System.Net métricas.
Coletar métricas System.Net
Há duas partes para usar métricas em um aplicativo .NET:
- Instrumentação: O código em bibliotecas .NET faz medições e associa essas medições a um nome de métrica. O .NET e o ASP.NET Core incluem muitas métricas internas.
- Coleção: um aplicativo .NET configura métricas nomeadas a serem transmitidas do aplicativo para armazenamento e análise externos. Algumas ferramentas podem executar a configuração fora do aplicativo usando arquivos de configuração ou uma ferramenta de interface do usuário.
Esta seção demonstra vários métodos para coletar e exibir métricas System.Net.
Aplicativo de exemplo
Para este tutorial, crie um aplicativo simples que envie solicitações HTTP para vários pontos de extremidade em paralelo.
dotnet new console -o HelloBuiltinMetrics
cd ..\HelloBuiltinMetrics
Substitua o conteúdo do pelo seguinte código de Program.cs
exemplo:
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.");
});
}
Veja métricas com contadores de pontos
dotnet-counters
é uma ferramenta de monitoramento de desempenho multiplataforma para monitoramento de integridade ad-hoc e investigação de desempenho de primeiro nível.
dotnet tool install --global dotnet-counters
Ao executar em um processo .NET 8+, dotnet-counters
habilita os instrumentos definidos pelo --counters
argumento e exibe as medidas. Ele atualiza continuamente o console com os números mais recentes:
dotnet-counters monitor --counters System.Net.Http,System.Net.NameResolution -n HelloBuiltinMetrics
Veja métricas no Grafana com OpenTelemetry e Prometheus
Descrição geral
- É um projeto de código aberto neutro do fornecedor suportado pela Cloud Native Computing Foundation.
- Padroniza a geração e coleta de telemetria para software nativo da nuvem.
- Funciona com .NET usando as APIs de métrica .NET.
- É endossado pelo Azure Monitor e por muitos fornecedores de APM.
Este tutorial mostra uma das integrações disponíveis para métricas OpenTelemetry usando os projetos OSS Prometheus e Grafana . O fluxo de dados de métricas consiste nas seguintes etapas:
As APIs de métrica .NET registram medições do aplicativo de exemplo.
A biblioteca OpenTelemetry em execução no aplicativo agrega as medidas.
A biblioteca de exportadores Prometheus disponibiliza os dados agregados por meio de um ponto de extremidade de métricas HTTP. 'Exportador' é o que a OpenTelemetry chama de bibliotecas que transmitem telemetria para back-ends específicos do fornecedor.
Um servidor Prometheus:
- Sonda o ponto de extremidade da métrica.
- Lê os dados.
- Armazena os dados em um banco de dados para persistência de longo prazo. Prometheus refere-se à leitura e armazenamento de dados como raspagem de um ponto de extremidade.
- Pode ser executado em uma máquina diferente.
O servidor Grafana:
- Consulta os dados armazenados no Prometheus e os exibe em um painel de monitoramento baseado na Web.
- Pode ser executado em uma máquina diferente.
Configurar o aplicativo de exemplo para usar o exportador Prometheus da OpenTelemetry
Adicione uma referência ao exportador OpenTelemetry Prometheus ao aplicativo de exemplo:
dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener --prerelease
Nota
Este tutorial usa uma compilação de pré-lançamento do suporte Prometheus da OpenTelemetry, disponível no momento da escrita.
Atualize Program.cs
com a configuração OpenTelemetry :
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.");
});
}
No código anterior:
AddMeter("System.Net.Http", "System.Net.NameResolution")
configura o OpenTelemetry para transmitir todas as métricas coletadas pelo built-inSystem.Net.Http
eSystem.Net.NameResolution
medidores.AddPrometheusHttpListener
configura o OpenTelemetry para expor o ponto de extremidade HTTP das métricas do Prometheus na porta9184
.
Nota
Essa configuração difere para aplicativos ASP.NET Core, onde as métricas são exportadas com OpenTelemetry.Exporter.Prometheus.AspNetCore
em vez de HttpListener
. Veja o exemplo relacionado ASP.NET Core.
Execute o aplicativo e deixe-o em execução para que as medições possam ser coletadas:
dotnet run
Instalar e configurar o Prometheus
Siga os primeiros passos do Prometheus para configurar um servidor Prometheus e confirme se está a funcionar.
Modifique o arquivo de configuração prometheus.yml para que o Prometheus raspe o ponto de extremidade de métricas que o aplicativo de exemplo está expondo. Adicione o seguinte texto realçado na scrape_configs
secção:
# 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']
Iniciar o Prometheus
Recarregue a configuração ou reinicie o servidor Prometheus.
Confirme se OpenTelemetryTest está no estado UP na página Status>Targets do portal da Web Prometheus.
Na página Gráfico do portal Web Prometheus, introduza
http
a caixa de texto da expressão e selecionehttp_client_active_requests
. Na guia gráfico, Prometheus mostra ohttp.client.active_requests
valor do contador emitido pelo aplicativo de exemplo.
Mostrar métricas em um painel do Grafana
Siga as instruções padrão para instalar o Grafana e conectá-lo a uma fonte de dados Prometheus.
Crie um painel do Grafana selecionando o + ícone na barra de ferramentas superior e, em seguida, selecionando Painel. No editor de painel exibido, digite Abrir conexões HTTP/1.1 na caixa Título e a seguinte consulta no campo de expressão PromQL:
sum by(http_connection_state) (http_client_open_connections{network_protocol_version="1.1"})
- Selecione Aplicar para salvar e exibir o novo painel. Ele exibe o número de conexões HTTP/1.1 ativas vs ociosas no pool.
Melhoramento
Enriquecimento é a adição de tags personalizadas (também conhecidas como atributos ou rótulos) a uma métrica. Isso é útil se um aplicativo quiser adicionar uma categorização personalizada a painéis ou alertas criados com métricas.
O http.client.request.duration
instrumento apoia o enriquecimento registrando retornos de chamada com o HttpMetricsEnrichmentContext.
Observe que esta é uma API de baixo nível e um registro de retorno de chamada separado é necessário para cada HttpRequestMessage
.
Uma maneira simples de fazer o registro de retorno de chamada em um único local é implementar um arquivo DelegatingHandler. Isso permitirá que você intercete e modifique as solicitações antes que elas sejam encaminhadas para o manipulador interno e enviadas ao servidor:
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 você estiver trabalhando com IHttpClientFactory
o , você pode usar AddHttpMessageHandler para registrar o 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
Por motivos de desempenho, o retorno de chamada de enriquecimento só é invocado quando o http.client.request.duration
instrumento está habilitado, o que significa que algo deve estar coletando as métricas.
Pode ser dotnet-monitor
, exportador de Prometheus, um MeterListener
, ou um MetricCollector<T>
.
IMeterFactory
e IHttpClientFactory
integração
As métricas HTTP foram projetadas com isolamento e capacidade de teste em mente. Esses aspetos são suportados pelo uso do , que permite a publicação de IMeterFactorymétricas por uma instância personalizada Meter para manter os medidores isolados uns dos outros.
Por padrão, todas as métricas são emitidas por um interno global Meter para a System.Net.Http
biblioteca. Esse comportamento pode ser substituído atribuindo uma instância personalizada IMeterFactory a SocketsHttpHandler.MeterFactory ou HttpClientHandler.MeterFactory.
Nota
O Meter.Name é System.Net.Http
para todas as métricas emitidas por HttpClientHandler
e SocketsHttpHandler
.
Ao trabalhar com Microsoft.Extensions.Http
e IHttpClientFactory
no .NET 8+, a implementação padrão IHttpClientFactory
seleciona automaticamente a IMeterFactory
instância registrada no IServiceCollection e a atribui ao manipulador primário que cria internamente.
Nota
A partir do .NET 8, o AddHttpClient método chama AddMetrics automaticamente para inicializar os serviços de métricas e registrar a implementação padrão IMeterFactory com IServiceCollectiono . O padrão IMeterFactory armazena Meter instâncias em cache por nome, o que significa que haverá uma Meter com o nome System.Net.Http
per IServiceCollection.
Métricas de teste
O exemplo a seguir demonstra como validar métricas internas em testes de unidade usando xUnit IHttpClientFactory
e MetricCollector<T>
do Microsoft.Extensions.Diagnostics.Testing
pacote NuGet:
[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"]);
});
}
Métricas vs. Contadores de Eventos
As métricas são mais ricas em recursos do que os EventCounters, principalmente por causa de sua natureza multidimensional. Essa multidimensionalidade permite criar consultas sofisticadas em ferramentas como o Prometheus e obter insights em um nível que não é possível com o EventCounters.
No entanto, a partir do .NET 8, apenas os System.Net.Http
componentes e são System.Net.NameResolutions
instrumentados usando métricas, o que significa que, se você precisar de contadores dos níveis inferiores da pilha, como System.Net.Sockets
ou System.Net.Security
, deverá usar EventCounters.
Além disso, existem algumas diferenças semânticas entre Metrics e seus EventCounters correspondentes.
Por exemplo, ao usar HttpCompletionOption.ResponseContentRead
o , o current-requests
EventCounter considera uma solicitação ativa até o momento em que o último byte do corpo da solicitação foi lido.
Sua contrapartida http.client.active_requests
de métricas não inclui o tempo gasto lendo o corpo da resposta ao contar as solicitações ativas.
Precisa de mais métricas?
Se você tiver sugestões para outras informações úteis que possam ser expostas por meio de métricas, crie um problema dotnet/runtime.