Инициализировать репозиторий «Дейл»
Первый коммит: модульный монолит ядра (.NET 10) и gRPC-сервисы ai/ml/telegram, фронтенд Vue 3/Vite/Tailwind, документация (ТЗ, инструкция пользователя, техдокументация, код-стайл), бэклог, скрипты развёртывания и архив прототипа LeadRadar.
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using OpenTelemetry;
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user