Сетевые метрики в .NET
Метрики — это числовые измерения, сообщаемые с течением времени. Обычно они используются для мониторинга работоспособности приложения и создания оповещений.
Начиная с .NET 8, System.Net.Http
System.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.
Обзор
- Является проектом с открытым исходным кодом, нейтральным поставщиком, поддерживаемым Cloud Native Computing Foundation.
- Стандартизирует создание и сбор данных телеметрии для облачного программного обеспечения.
- Работает с .NET с помощью API метрик .NET.
- Поддерживается Azure Monitor и многими поставщиками APM.
В этом руководстве показана одна из интеграции, доступная для метрик OpenTelemetry с помощью проектов OSS Prometheus и Grafana . Поток данных метрик состоит из следующих шагов:
Api метрик .NET записывают измерения из примера приложения.
Библиотека OpenTelemetry, запущенная в приложении, объединяет измерения.
Библиотека программы экспорта Prometheus предоставляет доступ к агрегированным данным через конечную точку метрик HTTP. Программа экспорта — это библиотеки OpenTelemetry, передающие телеметрию в серверные части конкретных поставщиков.
Сервер Prometheus:
- Опрашивает конечную точку метрик.
- Считывает данные.
- Сохраняет данные в базе данных для долгосрочного сохранения. Prometheus относится к чтению и хранению данных в качестве очистки конечной точки.
- Может работать на другом компьютере.
Сервер 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
Перезагрузите конфигурацию или перезапустите сервер Prometheus.
Убедитесь, что OpenTelemetryTest находится в состоянии UP на странице "Целевые показатели состояния>" веб-портала Prometheus.
На странице Graph веб-портала Prometheus введите
http
текстовое поле выражения и выберитеhttp_client_active_requests
. На вкладке графа Prometheus отображается значение счетчикаhttp.client.active_requests
, созданного примером приложения.
Отображение метрик на панели мониторинга Grafana
Следуйте стандартным инструкциям по установке решения Grafana и его подключению к источнику данных Prometheus.
Создайте панель мониторинга Grafana, выбрав + значок на верхней панели инструментов и выбрав панель мониторинга. В появившемся редакторе панели мониторинга введите open HTTP/1.1 Подключение ions в поле "Заголовок" и следующий запрос в поле выражения PromQL:
sum by(http_connection_state) (http_client_open_connections{network_protocol_version="1.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.Http
IHttpClientFactory
.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.Http
System.Net.NameResolutions
компоненты инструментируются с помощью метрик, то есть если вам нужны счетчики из более низких уровней стека, например System.Net.Sockets
или System.Net.Security
, необходимо использовать EventCounters.
Кроме того, существуют некоторые семантические различия между метриками и соответствующими значениями EventCounters.
Например, при использовании HttpCompletionOption.ResponseContentRead
current-requests
eventCounter считает запрос активным до момента чтения последнего байта текста запроса.
Его аналог http.client.active_requests
метрик не включает время, затраченное на чтение текста ответа при подсчете активных запросов.
Требуются дополнительные метрики?
Если у вас есть предложения по другим полезным сведениям, которые могут быть предоставлены с помощью метрик, создайте проблему dotnet/runtime.