Передача TraceId (CorrelationId) между сервисами ASP.NET Core

Просто о NET | создано: 11.04.2020 | опубликовано: 11.04.2020 | обновлено: 13.01.2024 | просмотров: 7962

Библиотека, которая призвана упростить передачу TraceId (CorrelationId) между микросервисами.

Что такое TraceId

 В микросервисной архитектуре много сервисов по определению. Значит одна бизнес-операция может "пролегать" через несколько сервисов, а значит множество REST-запросов "полетят" по разным WebAPI. Например, оплата в интернет-магазине товара является бизнес-операцией, которая может состоять из нескольких запросов: оплата счета, отправка уведомлений, сбор статистики и т.д. И каждый из перечисленных запросов может (и должен) обрабатываться отдельным сервисом. Вопрос, как фильтровать все запросы для одной бизнес-операции чтобы отследить результаты? Ответ - надо чтобы все запросы были помечены одной и той же меткой (идентификатором). Такой идентификатор обычно называют CorrelationId (я называю TraceId, потому что так короче и писать проще).

Как это работает

Работает система на базе заголовков (headers)  запросов. И в REST-запросах, и в MessageQueue-запросах...  Почему используются "заголовки"? Перефразирую вопрос, почему нельзя сделать ViewModel, к котором будет TraceId? Тезисно:

  • Потому что эта операция стоит над бизнес-логикой и, следовательно, не должна смешиваться.
  • Потому что ваши ViewModels будут меняться, появляться, исчезать, а запросы изменяются очень и очень редко.
  • Потому что обработка TraceId происходит в том числе и другими сервисами. Например, для сбора статистики или мониторинга. А ваши ViewModels никто "залезать" не будет.

Как реализовать в ASP.NET Core?

Уже готовых решений в интернете полным-полно. Я использую библиотеку Calabonga.Microservices.Tracker, которой решил поделиться совершенно недавно. Как это работает.

  1. Установливаете nuget-пакет
  2. Настраиваете пару параметров
  3. И вуаля!

Настройка в методе ConfigureServices

Настройка достаточно гибкая и имеет несколько варинатов.

Вариант 1: Самый простой

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    // Omitted for brevity
    ...

    services.AddCommunicationTracker();

    ...
}

Вариант 2: Установка своих параметров

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    // Omitted for brevity
    ...

    services.AddCommunicationTracker<DefaultTrackerIdGenerator>(options =>
        {
            options.TrackerIdGenerator = () => "{ВАШ ГЕНЕРАТОР КОДА}";
            options.EnforceHeader = false;
            options.IgnoreRequestHeader = false;
            options.RequestHeaderName = "X-Custom-Request-Trace-ID"; //Default is: X-Microservice-Trace-Id
            options.ResponseHeaderName = "X-Custom-Response-Trace-ID"; //Default is: X-Microservice-Trace-Id
            options.AddToLogger = true;
            options.LoggerScopeName = "MICROSERVICE_LOGGER";
            options.IncludeInResponse = true;
            options.UpdateTraceIdentifier = true;
        });

    ...
}

Обратите внимание на класс DefaultTrackerIdGenerator. Это генератор идентификторов по типу Guid, который встроен в сборку Calabonga.Microservices.Tracker. Если вы хотети использовать свой собственный генератор, то его можно сделать например так:

public class CustomTrackerIdGenerator : ITrackerIdGenerator
{
    /// <summary>
    /// Generates a tracker ID string for the current request.
    /// </summary>
    /// <param name="context">The <see cref="Microsoft.AspNetCore.Http.HttpContext"/> of the current request.</param>
    /// <returns>A string representing the tracker ID.</returns>
    public string GenerateTrackerId(HttpContext context)
    {
        return $"{TokenGenerator.Generate(11)}-{TokenGenerator.Generate(11)}";
    }
}

где TokenGenerator.Generate - это helper из сборки Calabonga.TokenGeneratorCore. И таким образом, можно этот генератор использовать так:

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    // Omitted for brevity
    ...

    services.AddCommunicationTracker<CustomTrackerIdGenerator>();

    ...
}

Как получить TraceId

Нужно внедрить зависимость `ITrackerContextAccessor ` и обратиться к контексту:

public class ProcessingEndpoints : AppDefinition
{
    public override void ConfigureApplication(WebApplication app)
    {
        app.MapGet("/api/processions/start-validation/{processingId:guid}", StartValidation);
    }

    [ProducesResponseType(200)]
    [ProducesResponseType(401)]
    [Authorize(AuthenticationSchemes = AuthData.AuthSchemes)]
    [FeatureGroupName("Processions")]
    private async Task<IResult> StartValidation(Guid processingId, [FromServices] ITrackerContextAccessor trackerContext, [FromServices] IMediator mediator, HttpContext context)
    {
        var correlationId = trackerContext.TrackerContext.TrackerId;
        var result = await mediator.Send(new ProcessingValidate.Request(processingId, Guid.Parse(correlationId)), context.RequestAborted);
        return Results.Ok(result);
    }
}

 

Правда, ничего сложного?

Настройка в методе Configure

Тут еще проще...

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
    ...

    app.UseCommunicationTracker();

    ...
}

Готово! Всё настроено для методов, но если вы захотите использовать HttpClient, то надо еще добавить его настройку в метод ConfigureServices:

services.AddHttpClient("MyClient").AddCommunicationTrackerForwarding();

Это скажет системе, чтобы она "прокидывала" TraceId в вызовы черезе HttpClient.

Заключение

Правда же ничего сложного? Будут вопросы - пишите! Почитаю...