Прогон dotnet format (IDE0005) по 4 решениям: удалены лишние using, оставшиеся после миграции namespace (676 файлов).
95 lines
5.5 KiB
C#
95 lines
5.5 KiB
C#
using Microsoft.AspNetCore.Builder;
|
||
using Microsoft.AspNetCore.Hosting;
|
||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||
using Microsoft.Extensions.DependencyInjection;
|
||
using OpenTelemetry.Metrics;
|
||
|
||
namespace Deal.Grpc.Hosting;
|
||
|
||
/// <summary>
|
||
/// Общая настройка метрик Deal-сервисов (этап 12, пакет A): OpenTelemetry → экспортёр Prometheus,
|
||
/// эндпоинт <c>/metrics</c> в отдельном HTTP/1.1 Kestrel-эндпоинте (порт 9464 по умолчанию).
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// <para>
|
||
/// gRPC-сервисы слушают HTTP/2 (см. <see cref="GrpcServer.ConfigureKestrelHttp2Endpoint"/>), а
|
||
/// Prometheus scrape'ит обычным HTTP/1.1-запросом GET — поэтому метрики вынесены на отдельный
|
||
/// Kestrel-эндпоинт с <see cref="HttpProtocols.Http1"/>: тот же процесс, тот же DI, но отдельный порт.
|
||
/// Порт не публикуется наружу — scrape идёт внутри compose-сети от сервиса <c>prometheus</c>.
|
||
/// </para>
|
||
/// <para>
|
||
/// Сервисы вызывают ровно две строки: <see cref="AddDealMetrics"/> — на этапе сборки хоста (до
|
||
/// <c>builder.Build()</c>, обычно из <c>configureBuilder</c>-хука Program.cs), и
|
||
/// <see cref="MapDealMetrics"/> — после сборки. Инструментация (входящие ASP.NET Core/gRPC,
|
||
/// исходящие HTTP/gRPC) даёт метрики RPS/латентности/ошибок без ручного кода; прикладные метрики
|
||
/// (токены, аудит, очереди) добавляет ядро своим meter'ом <see cref="MeterName"/>.
|
||
/// </para>
|
||
/// </remarks>
|
||
public static class DealMetricsHosting
|
||
{
|
||
/// <summary>
|
||
/// Имя meter'а прикладных метрик Deal (общий префикс с ядром: <c>deal.*</c>).
|
||
/// </summary>
|
||
public const string MeterName = "Deal";
|
||
|
||
/// <summary>
|
||
/// Порт эндпоинта <c>/metrics</c> по умолчанию (конвенция OpenTelemetry Prometheus).
|
||
/// </summary>
|
||
public const int DefaultMetricsPort = 9464;
|
||
|
||
// Env-ключ порта метрик (переопределяет DefaultMetricsPort).
|
||
private const string MetricsPortEnvKey = "METRICS_PORT";
|
||
|
||
/// <summary>
|
||
/// Порт эндпоинта метрик: env <c>METRICS_PORT</c> (заданное нечисловое значение игнорируется),
|
||
/// иначе <see cref="DefaultMetricsPort"/>. Локальный запуск нескольких процессов на хосте без
|
||
/// compose требует разных значений (в compose порты контейнеров изолированы).
|
||
/// </summary>
|
||
/// <param name="defaultPort">Дефолтный порт (обычно <see cref="DefaultMetricsPort"/>).</param>
|
||
/// <returns>Порт HTTP/1.1-эндпоинта метрик.</returns>
|
||
public static int ResolveMetricsPort(int defaultPort)
|
||
=> int.TryParse(Environment.GetEnvironmentVariable(MetricsPortEnvKey), out int port) && port > 0
|
||
? port
|
||
: defaultPort;
|
||
|
||
/// <summary>
|
||
/// Регистрирует OTel-метрики и Kestrel-эндпоинт метрик (HTTP/1.1, 0.0.0.0:<paramref name="metricsPort"/>).
|
||
/// Вызывать до <c>builder.Build()</c>.
|
||
/// </summary>
|
||
/// <param name="builder">Билдер хоста сервиса.</param>
|
||
/// <param name="metricsPort">Порт HTTP/1.1-эндпоинта метрик.</param>
|
||
public static void AddDealMetrics(WebApplicationBuilder builder, int metricsPort)
|
||
{
|
||
ArgumentNullException.ThrowIfNull(builder);
|
||
|
||
// Отдельный HTTP/1.1-эндпоинт для scrape: gRPC-порт остаётся строго HTTP/2 (см. remarks класса).
|
||
builder.WebHost.ConfigureKestrel(kestrel =>
|
||
{
|
||
kestrel.ListenAnyIP(metricsPort, listen => listen.Protocols = HttpProtocols.Http1);
|
||
});
|
||
|
||
// Инструментация входящих запросов (http.server.*: RPS/латентность/ошибки по route) и исходящих
|
||
// HTTP-клиентов (http.client.*) + экспортёр Prometheus. AddMeter — прикладные метрики.
|
||
// Исходящие gRPC-вызовы (пакет Instrumentation.GrpcNetClient) дают трейс-инструментацию, а не
|
||
// метрики — в MeterProviderBuilder не добавляются (для клиентских метрик gRPC-хопа хватает
|
||
// серверной стороны соответствующего сервиса).
|
||
builder.Services
|
||
.AddOpenTelemetry()
|
||
.WithMetrics(metrics => metrics
|
||
.AddMeter(MeterName)
|
||
.AddAspNetCoreInstrumentation()
|
||
.AddHttpClientInstrumentation()
|
||
.AddPrometheusExporter());
|
||
}
|
||
|
||
/// <summary>
|
||
/// Мапит эндпоинт <c>/metrics</c> (формат Prometheus). Вызывать после <c>builder.Build()</c>.
|
||
/// </summary>
|
||
/// <param name="app">Собранное приложение сервиса.</param>
|
||
public static void MapDealMetrics(WebApplication app)
|
||
{
|
||
ArgumentNullException.ThrowIfNull(app);
|
||
app.MapPrometheusScrapingEndpoint();
|
||
}
|
||
}
|