Поделиться через


Сетевые метрики в .NET

Метрики — это числовые измерения, сообщаемые с течением времени. Обычно они используются для мониторинга работоспособности приложения и создания оповещений.

Начиная с .NET 8, System.Net.HttpSystem.Net.NameResolution компоненты инструментируются для публикации метрик с помощью. Новый API System.Diagnostics.Metrics в NET. Эти метрики были разработаны в сотрудничестве с OpenTelemetry , чтобы убедиться, что они соответствуют стандарту и хорошо работают с популярными инструментами, такими как Prometheus и Grafana. Они также являются многомерными, что означает, что измерения связаны с парами "ключ-значение", называемыми тегами (атрибуты или метки", которые позволяют классифицировать данные для анализа.

Совет

Полный список всех встроенных инструментов вместе с их атрибутами см. в System.Net метрик.

Сбор метрик System.Net

Использование метрик в приложении .NET состоит из двух этапов:

  • Инструментирование: код в библиотеках .NET принимает измерения и связывает их с именем метрики. .NET и ASP.NET Core включают множество встроенных метрик.
  • Коллекция. Приложение .NET настраивает именованные метрики, передаваемые из приложения для внешнего хранилища и анализа. Некоторые средства могут выполнять конфигурацию за пределами приложения с помощью файлов конфигурации или средства пользовательского интерфейса.

В этом разделе показаны различные методы сбора и просмотра System.Net метрик.

Пример приложения

Для этого руководства создайте простое приложение, которое отправляет HTTP-запросы в различные конечные точки параллельно.

dotnet new console -o HelloBuiltinMetrics
cd ..\HelloBuiltinMetrics

Замените содержимое Program.cs следующим примером кода:

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

Просмотр метрик с помощью dotnet-counters

dotnet-counters — это кроссплатформенное средство мониторинга производительности для мониторинга работоспособности и исследования производительности первого уровня.

dotnet tool install --global dotnet-counters

При выполнении в процессе .NET 8+ включает инструменты, dotnet-counters определенные --counters аргументом, и отображает измерения. Она постоянно обновляет консоль с последними номерами:

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

Просмотр метрик в Grafana с помощью OpenTelemetry и Prometheus.

Обзор

OpenTelemetry:

  • Является проектом с открытым исходным кодом, нейтральным поставщиком, поддерживаемым Cloud Native Computing Foundation.
  • Стандартизирует создание и сбор данных телеметрии для облачного программного обеспечения.
  • Работает с .NET с помощью API метрик .NET.
  • Поддерживается Azure Monitor и многими поставщиками APM.

В этом руководстве показана одна из интеграции, доступная для метрик OpenTelemetry с помощью проектов OSS Prometheus и Grafana . Поток данных метрик состоит из следующих шагов:

  1. Api метрик .NET записывают измерения из примера приложения.

  2. Библиотека OpenTelemetry, запущенная в приложении, объединяет измерения.

  3. Библиотека программы экспорта Prometheus предоставляет доступ к агрегированным данным через конечную точку метрик HTTP. Программа экспорта — это библиотеки OpenTelemetry, передающие телеметрию в серверные части конкретных поставщиков.

  4. Сервер Prometheus:

    • Опрашивает конечную точку метрик.
    • Считывает данные.
    • Сохраняет данные в базе данных для долгосрочного сохранения. Prometheus относится к чтению и хранению данных в качестве очистки конечной точки.
    • Может работать на другом компьютере.
  5. Сервер Grafana:

    • Запрашивает данные, хранящиеся в Prometheus, и отображает его на веб-панели мониторинга.
    • Может работать на другом компьютере.

Настройка примера приложения для использования экспортера Prometheus в OpenTelemetry

Добавьте ссылку на экспортер Prometheus OpenTelemetry в пример приложения:

dotnet add package OpenTelemetry.Exporter.Prometheus.HttpListener --prerelease

Примечание.

В этом руководстве используется предварительная сборка поддержки Prometheus OpenTelemetry, доступная во время написания статьи.

Обновление Program.cs с помощью конфигурации 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.");
    });
}

В предыдущем коде:

  • AddMeter("System.Net.Http", "System.Net.NameResolution") настраивает OpenTelemetry для передачи всех метрик, собранных встроенными System.Net.Http и System.Net.NameResolution счетчиками.
  • AddPrometheusHttpListener настраивает OpenTelemetry для предоставления конечной точки HTTP метрик Prometheus через порт 9184.

Примечание.

Эта конфигурация отличается для приложений ASP.NET Core, где метрики экспортируются OpenTelemetry.Exporter.Prometheus.AspNetCore вместо HttpListener. См. соответствующий пример ASP.NET Core.

Запустите приложение и оставьте его запущенным, чтобы можно было собирать измерения:

dotnet run

Установка и настройка Prometheus

Выполните первые действия Prometheus, чтобы настроить сервер Prometheus и подтвердить его работу.

Измените файл конфигурации prometheus.yml , чтобы Prometheus сломать конечную точку метрик, которую представляет пример приложения. Добавьте следующий выделенный текст в scrape_configs разделе:

# 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']

Запуск prometheus

  1. Перезагрузите конфигурацию или перезапустите сервер Prometheus.

  2. Убедитесь, что OpenTelemetryTest находится в состоянии UP на странице "Целевые показатели состояния>" веб-портала Prometheus. Prometheus status

  3. На странице Graph веб-портала Prometheus введите http текстовое поле выражения и выберите http_client_active_requests. http_client_active_requests На вкладке графа Prometheus отображается значение счетчика http.client.active_requests , созданного примером приложения. Prometheus active requests graph

Отображение метрик на панели мониторинга Grafana

  1. Следуйте стандартным инструкциям по установке решения Grafana и его подключению к источнику данных Prometheus.

  2. Создайте панель мониторинга Grafana, выбрав + значок на верхней панели инструментов и выбрав панель мониторинга. В появившемся редакторе панели мониторинга введите open HTTP/1.1 Подключение ions в поле "Заголовок" и следующий запрос в поле выражения PromQL:

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

Grafana HTTP/1.1 Connections

  1. Нажмите кнопку "Применить" , чтобы сохранить и просмотреть новую панель мониторинга. В нем отображается количество активных и неактивных подключений HTTP/1.1 в пуле.

Обогащение

Обогащение — это добавление пользовательских тегов (атрибутов или меток) к метрикам. Это полезно, если приложение хочет добавить настраиваемую категорию на панели мониторинга или оповещения, созданные с помощью метрик. Инструмент http.client.request.duration поддерживает обогащение путем регистрации обратных вызовов с помощью HttpMetricsEnrichmentContextинструмента. Обратите внимание, что это низкоуровневый API, для каждого из которых HttpRequestMessageтребуется отдельная регистрация обратного вызова.

Простой способ выполнить регистрацию обратного вызова в одном месте — реализовать настраиваемый DelegatingHandler. Это позволит перехватывать и изменять запросы, прежде чем они перенаправляются во внутренний обработчик и отправляются на сервер:

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

Если вы работаете с IHttpClientFactoryним, можно использовать AddHttpMessageHandler для регистрации 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");

Примечание.

По соображениям производительности обратный вызов обогащения вызывается только в том случае, если http.client.request.duration инструмент включен, то есть что-то должно собирать метрики. Это может быть dotnet-monitorэкспортер Prometheus, a MeterListenerили a MetricCollector<T>.

IMeterFactory и IHttpClientFactory интеграция

Метрики HTTP были разработаны с учетом изоляции и тестирования. Эти аспекты поддерживаются использованием IMeterFactory, что позволяет публиковать метрики пользовательским Meter экземпляром, чтобы обеспечить изоляцию счетчиков друг от друга. По умолчанию все метрики создаются глобальной Meter внутренней библиотекой System.Net.Http . Это поведение можно переопределить, назначив настраиваемый IMeterFactory экземпляр SocketsHttpHandler.MeterFactory или HttpClientHandler.MeterFactory.

Примечание.

Используется Meter.NameSystem.Net.Http для всех метрик, HttpClientHandler создаваемых и SocketsHttpHandler.

При работе с Microsoft.Extensions.HttpIHttpClientFactory .NET 8+ реализация по умолчанию IHttpClientFactory автоматически выбирает IMeterFactory экземпляр, зарегистрированный в нем IServiceCollection , и назначает его основному обработчику, который он создает внутри.

Примечание.

Начиная с .NET 8 AddHttpClient метод автоматически вызывает AddMetrics службы метрик и регистрирует реализацию IServiceCollectionпо умолчаниюIMeterFactory. Экземпляры по умолчанию IMeterFactory кэшируются Meter по имени, то есть будет один Meter с именем System.Net.Http для каждого IServiceCollection.

Тестовые метрики

В следующем примере показано, как проверить встроенные метрики в модульных тестах с помощью xUnit, IHttpClientFactoryа также MetricCollector<T> из Microsoft.Extensions.Diagnostics.Testing пакета 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"]);
        });
}

Метрики и событияCounters

Метрики являются более функциональными , чем EventCounters, в основном из-за их многомерной природы. Эта многомерность позволяет создавать сложные запросы в таких инструментах, как Prometheus, и получать аналитические сведения на уровне, который невозможно использовать в EventCounters.

Тем не менее, по состоянию на .NET 8, только System.Net.HttpSystem.Net.NameResolutions компоненты инструментируются с помощью метрик, то есть если вам нужны счетчики из более низких уровней стека, например System.Net.Sockets или System.Net.Security, необходимо использовать EventCounters.

Кроме того, существуют некоторые семантические различия между метриками и соответствующими значениями EventCounters. Например, при использовании HttpCompletionOption.ResponseContentReadcurrent-requests eventCounter считает запрос активным до момента чтения последнего байта текста запроса. Его аналог http.client.active_requests метрик не включает время, затраченное на чтение текста ответа при подсчете активных запросов.

Требуются дополнительные метрики?

Если у вас есть предложения по другим полезным сведениям, которые могут быть предоставлены с помощью метрик, создайте проблему dotnet/runtime.