Разбить Infrastructure и корень Deal.Api по назначению
Integrations -> Abstractions/Exceptions/Extensions/Models/Options/ Services (включая Storage); Persistence-конфигурации -> Configurations; корень Deal.Api (оркестратор/планировщики/DTO) -> Services/Dtos. namespace приведён к путям, using добавлены/дедуплицированы, FQN обновлены.
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
using Grpc.Core;
|
||||
using Grpc.Health.V1;
|
||||
using Grpc.Net.Client;
|
||||
using Deal.Infrastructure.Integrations.Abstractions;
|
||||
using Deal.Infrastructure.Integrations.Exceptions;
|
||||
using Deal.Infrastructure.Integrations.Extensions;
|
||||
using Deal.Infrastructure.Integrations.Models;
|
||||
using Deal.Infrastructure.Integrations.Options;
|
||||
|
||||
namespace Deal.Infrastructure.Integrations.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Health-проба grpc.health.v1 автономных сервисов (ml/ai/telegram) для операторского health
|
||||
/// (план Task 10: GET /api/operator/health, Ruling 3/6/9).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Каждый вызов строит свой короткоживущий канал к <c>endpoint</c> сервиса (dev — без TLS, Ruling 2;
|
||||
/// mTLS (Ruling 6, Task 13): при включённом флаге канал подписывает запрос клиентским сертификатом и
|
||||
/// проверяет CA сервера — сертификаты передаются <see cref="MtlsCertificates"/> в конструктор) и спрашивает
|
||||
/// Health.Check("") с дедлайном 3 с — health не должен висеть дольше таймаута. Классификация: ответ
|
||||
/// <c>SERVING</c> → Reachable+Serving; ответ с иным статусом → Reachable без
|
||||
/// Serving; таймаут/нет соединения (Unavailable/DeadlineExceeded, HTTP-транспорт) и сервис без health-контракта
|
||||
/// (Unimplemented) → <see cref="ServiceHealthResult.Unreachable"/>.
|
||||
/// </remarks>
|
||||
public sealed class ServiceHealthProbe
|
||||
{
|
||||
/// <summary>
|
||||
/// Дедлайн health-RPC, секунд (Ruling 3/9: операторский health отвечает за ~3 с на сервис).
|
||||
/// </summary>
|
||||
public const int HealthTimeoutSeconds = 3;
|
||||
|
||||
private readonly MtlsCertificates? _mtlsCertificates;
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт пробу; mTLS-каналы — при переданных сертификатах (иначе plaintext, dev).
|
||||
/// </summary>
|
||||
/// <param name="mtlsCertificates">Сертификаты mTLS (Ruling 6, Task 13): null — plaintext-канал.</param>
|
||||
public ServiceHealthProbe(MtlsCertificates? mtlsCertificates = null)
|
||||
{
|
||||
_mtlsCertificates = mtlsCertificates;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверяет health-контракт gRPC-сервиса по базовому адресу (grpc.health.v1, сервис "").
|
||||
/// </summary>
|
||||
/// <param name="endpoint">Базовый адрес сервиса (http://host:port; пустой/пробельный — ошибка аргумента).</param>
|
||||
/// <param name="ct">Токен отмены вызывающего.</param>
|
||||
/// <returns>Результат пробы (см. <see cref="ServiceHealthResult"/>).</returns>
|
||||
public async Task<ServiceHealthResult> ProbeAsync(string endpoint, CancellationToken ct)
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(endpoint);
|
||||
using SocketsHttpHandler? handler = _mtlsCertificates?.CreateClientHttpHandler();
|
||||
GrpcChannelOptions channelOptions = new() { MaxRetryAttempts = 0, HttpHandler = handler };
|
||||
using var channel = GrpcChannel.ForAddress(endpoint, channelOptions);
|
||||
var client = new Health.HealthClient(channel);
|
||||
try
|
||||
{
|
||||
HealthCheckResponse response = await client.CheckAsync(
|
||||
new HealthCheckRequest { Service = string.Empty },
|
||||
deadline: DateTime.UtcNow.AddSeconds(HealthTimeoutSeconds),
|
||||
cancellationToken: ct);
|
||||
return new ServiceHealthResult(
|
||||
Reachable: true,
|
||||
Serving: response.Status == HealthCheckResponse.Types.ServingStatus.Serving);
|
||||
}
|
||||
catch (RpcException exception) when (exception.IsCommunicationFailure())
|
||||
{
|
||||
return ServiceHealthResult.Unreachable;
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
// Ошибка транспорта HTTP/2 (DNS/соединение) — до gRPC-статуса не дошло.
|
||||
return ServiceHealthResult.Unreachable;
|
||||
}
|
||||
catch (OperationCanceledException) when (!ct.IsCancellationRequested)
|
||||
{
|
||||
// Сработал дедлайн пробы (отмена вызывающего выше пробросилась бы дальше) — сервис не ответил за 3 с.
|
||||
return ServiceHealthResult.Unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user