Partilhar via


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

OpenTelemetria:

  • É 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:

  1. As APIs de métrica .NET registram medições do aplicativo de exemplo.

  2. A biblioteca OpenTelemetry em execução no aplicativo agrega as medidas.

  3. 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.

  4. 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.
  5. 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-in System.Net.Http e System.Net.NameResolution medidores.
  • AddPrometheusHttpListener configura o OpenTelemetry para expor o ponto de extremidade HTTP das métricas do Prometheus na porta 9184.

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

  1. Recarregue a configuração ou reinicie o servidor Prometheus.

  2. Confirme se OpenTelemetryTest está no estado UP na página Status>Targets do portal da Web Prometheus. Prometheus status

  3. Na página Gráfico do portal Web Prometheus, introduza http a caixa de texto da expressão e selecione http_client_active_requests. http_client_active_requests Na guia gráfico, Prometheus mostra o http.client.active_requests valor do contador emitido pelo aplicativo de exemplo. Prometheus active requests graph

Mostrar métricas em um painel do Grafana

  1. Siga as instruções padrão para instalar o Grafana e conectá-lo a uma fonte de dados Prometheus.

  2. 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"})

Grafana HTTP/1.1 Connections

  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 IHttpClientFactoryo , 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 IHttpClientFactorye 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.ResponseContentReado , 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.