Regjistrimi në një mjedis mikroservice .Net në praktikë

Regjistrimi në një mjedis mikroservice .Net në praktikë

Regjistrimi është një mjet shumë i rëndësishëm për një zhvillues, por kur ndërton sisteme të shpërndara, ai bëhet një gur që duhet të vendoset pikërisht në themelin e aplikacionit tuaj, përndryshe kompleksiteti i zhvillimit të mikroshërbimeve do të ndihet shumë shpejt.

.Net Core 3 shtoi një të shkëlqyer aftësia për të kaluar kontekstin e korrelacionit në kokat e HTTP, kështu që nëse aplikacionet tuaja përdorin thirrje direkte HTTP për komunikim ndër-shërbimi, atëherë mund të përfitoni nga ky funksionalitet në kuti. Sidoqoftë, nëse arkitektura e backend-it tuaj përfshin ndërveprim përmes një ndërmjetësi mesazhesh (RabbitMQ, Kafka, etj.), atëherë ju duhet të kujdeseni vetë për temën e kalimit të kontekstit të korrelacionit përmes këtyre mesazheve.

Në këtë artikull, ne do të marrim një aplikacion të thjeshtë web api dhe do të organizojmë regjistrimin, gjë që do të ndodhë

  • mbani korrelacion nga fundi në fund midis regjistrave të shërbimeve të pavarura në mënyrë që të mund të shihni lehtësisht të gjitha aktivitetet që janë shkaktuar nga një kërkesë specifike nga klienti

  • keni një pikë të vetme hyrjeje me analizë të përshtatshme, në mënyrë që edhe Mbështetja të mund të përdorë mjetin e regjistrimit, për të cilin pyetjet si "Kam marrë një gabim me ID-në e kërkesës së tillë" u shfaqën në aplikacion

Së pari, ne duhet të vendosim për ofruesin e prerjeve në aplikacionin tonë. Kërkesa kryesore për prerjet moderne është struktura, d.m.th. nuk duhet të punojmë me mesazhe të sheshta, por me objekte. Falë regjistrave të tillë, ne mund të krijojmë lehtësisht pamje të mesazheve tona në seksione të ndryshme dhe të bëjmë analiza.

Për aplikimin tonë, ne do të përdorim paketën Serilog (Serilog), e cila ka mbështetje të shkëlqyer për prerjet strukturore dhe një sistem të pasur shtesë. Unë do të heq hapat bazë për vendosjen e tij (mund të gjeni një numër të madh artikujsh mbi këtë temë) dhe do të supozoj se

  • Serilogu është tashmë i konfiguruar dhe është regjistrimi i parazgjedhur i ofruesit tuaj të injektimit të varësisë

  • Pasurimi i mesazheve me vetitë e kontekstit është aktivizuar në konfigurimin e tij (Enrich.FromLogContext)

Hapi tjetër është të zgjidhni se në cilin sistem të centralizuar të regjistrimit do të dërgoni mesazhe nga Serilog. Ndoshta opsioni më i zakonshëm me burim të hapur sot është rafti ELK (Elasticsearch, Logstash dhe Kibana), dhe le ta marrim atë. Për ta bërë këtë, ne përdorim propozimin nga Logz.IO - pas regjistrimit për një plan falas, fuqia e plotë e motorit të kërkimit Lucene është në duart tona.

Na mbetet të shtojmë një paketë në projektin tonë Serilog.Sinks.Logzio

Install-Package Serilog.Sinks.Logzio

Dhe shtoni Enricher-in e duhur në konfigurimin tonë të logger-it duke e ushqyer atë me një shenjë aksesi

LoggerConfiguration loggerConfig = new LoggerConfiguration();
loggerConfig.WriteTo.Logzio(secrets.LogzioToken, 10, TimeSpan.FromSeconds(10), null, LogEventLevel.Debug);

Duke ekzekutuar aplikacionin, ne do të jemi në gjendje të vëzhgojmë mesazhet tona jo vetëm në tastierë, por edhe në Kibane.

Regjistrimi në një mjedis mikroservice .Net në praktikë

Interfaces

Regjistrimi në një mjedis mikroservice .Net në praktikë

Në një aplikacion të llojit të shërbimit, mund të dallohen dy ndërfaqe kryesore të ndërveprimit të tij me botën e jashtme; ne do t'i shënojmë ato si vertikale dhe horizontale. Një ndërfaqe vertikale është një api në internet përmes së cilës mbërrijnë thirrjet nga një aplikacion klienti. Horizontal është një ndërmjetës mesazhesh që përdoret për të shkëmbyer të dhëna me shërbime të tjera të brendshme.

Le të shqyrtojmë fazat e zbatimit të korrelacionit në secilën prej këtyre ndërfaqeve.

Korrelacioni në kërkesat HTTP

Për të marrë sa më shumë informacion, duhet të gjenerojmë një ID korrelacioni sa më afër fillimit të aktivitetit, d.m.th. në portë ose direkt në klient (mobil ose ueb). Meqenëse sot kemi të bëjmë me një aplikacion në fund të fundit, ne thjesht do të tregojmë mbi të kërkesën për titullin e detyrueshëm "X-Correlation-ID" në të gjitha kërkesat për ueb api.

Shtimi i një pakete ID e korrelacionit, funksioni i të cilit është të marrim vlerën nga titulli që na nevojitet

Install-Package CorrelationID

Shtojeni atë në tubacionin e përpunimit të kërkesës

public class Startup
{
    public void Configure(IApplicationBuilder application)
    {
        application
	    .UseCorrelationId(new CorrelationIdOptions
        {
            Header = "X-Correlation-ID",
            IncludeInResponse = false,
            UpdateTraceIdentifier = false,
            UseGuidForCorrelationId = false
        });
    }
}

Tani le të bëjmë një filtër të thjeshtë veprimi me të:

public sealed class ApiRequestFilter : ActionFilterAttribute
{
    public ApiRequestFilter(IApiRequestTracker apiRequestTracker, ICorrelationContextAccessor correlationContextAccessor)
    {
        _correlationContextAccessor = correlationContextAccessor ?? throw new ArgumentNullException(nameof(correlationContextAccessor));
    }
    
    private readonly ICorrelationContextAccessor _correlationContextAccessor;
    
    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        if (!Guid.TryParse(_correlationContextAccessor.CorrelationContext.CorrelationId, out Guid correlationId))
        {
            context.Result = new BadRequestResult();
            return;
        }
    
        await next.Invoke();
    }
    
    public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
    {
        await next.Invoke();
    }
}

Dhe shtoni atë në kontrollues

[Route("[controller]")]
[ApiController]
[ServiceFilter(typeof(ApiRequestFilter))]
public class CarsController : ControllerBase
{

}

Si rezultat, kontrolluesi do të shfaqë 400 kërkesë të keqe për të gjitha kërkesat pa një kokë me identifikuesin përkatës.

Pasi filluam të marrim një identifikues nga klienti, duhet ta shtojmë atë në kontekstin e regjistrimit, do të bëjmë një shtresë kornizë për këtë:

public class CorrelationIdContextLogger
{
    public CorrelationIdContextLogger(RequestDelegate next)
    {
        _next = next ?? throw new ArgumentNullException(nameof(next));
    }
    
    readonly RequestDelegate _next;
    
    public async Task InvokeAsync(HttpContext httpContext, ILogger<CorrelationIdContextLogger> logger, ICorrelationContextAccessor correlationContextAccessor)
    {
        if (Guid.TryParse(correlationContextAccessor.CorrelationContext.CorrelationId, out Guid correlationId))
        {
            using (logger.BeginScopeWith(("CorrelationId", correlationId)))
            {
                await _next(context);
            }
        }
        else
        {
            await _next(context);
        }
    }
}

Në aplikacionin tonë, ne përdorim standardin ILogger nga paketa Microsoft.Extensions.Logging.Abstractions, kështu që do t'i shtojmë një vlerë duke përdorur një shtesë të thjeshtë.

public static IDisposable BeginScopeWith(this ILogger logger, params (string key, object value)[] keys)
{
    return logger.BeginScope(keys.ToDictionary(x => x.key, x => x.value));
}

Ne shtojmë një shtresë në tubacionin e përpunimit të kërkesës dhe marrim rezultatin e dëshiruar.

public class Startup
{
    public void Configure(IApplicationBuilder application)
    {
        application.UseMiddleware<CorrelationIdContextLogger>();
    }
}

Tani të gjitha aktivitetet që krijohen nga kërkesat në api-në tonë të internetit përmbajnë një identifikues korrelacioni me të cilin ato mund të lidhen lehtësisht.

Regjistrimi në një mjedis mikroservice .Net në praktikë

Korrelacioni në mesazhet e ndërmjetësit

Hapi tjetër është konfigurimi i transmetimit dhe marrjes së identifikuesit të korrelacionit përmes ndërmjetësit të mesazheve. Në shembullin tonë, ne do të përdorim RabbitMQ, dhe si klient do të marrim kornizën MassTransit (MassTranzit). Përsëri, le të kapërcejmë konfigurimin fillestar për të punuar me MassTransit dhe të shkojmë drejtpërdrejt te konfigurimi i regjistrimit.

Për të filluar, ne mund të aktivizojmë regjistrat e vetë MassTransit, për këtë ne do të shtojmë një paketë në aplikacionin tonë MassTransit.SerilogIntegration

Install-Package MassTransit.SerilogIntegration

Tani, pasi të kemi shtuar regjistruesin në cilësimet e MassTransit, do të jemi në gjendje të shohim regjistrat e kornizës.

services
    .AddSingleton(provider =>
        {
            return Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                cfg.UseSerilog();
            });
        });

Lëreni aplikacionin tonë të dërgojë ngjarjen SomethingDoneMessage me vlerën "e kryer" si përgjigje ndaj kërkesës POST. Kontrata e një mesazhi të tillë mund të përshkruhet si më poshtë:

namespace MbMessages
{
    public interface ISomethingDoneMessageV1
    {
        string Value { get; }
    }
}

Mesazhet MassTransit janë në thelb një zarf që përmban mesazhe ndërmjetësi. Zarfi duket si ky:

{
  "messageId": "59020000-5dba-0015-10b8-08d77ec28593",
  "requestId": "59020000-5dba-0015-5674-08d77ec28592",
  "conversationId": "59020000-5dba-0015-bca8-08d77ec28594",
  "destinationAddress": "rabbitmq://bear.rmq.cloudamqp.com/aelzlsta/ya.servicetemplate.receiveendpoint",
  "headers": {},
  "messageType": [
    "urn:message:MbMessages:ISomethingDoneMessageV1"
  ],
  "message": {
    "value": "done"
  }
}

Mesazhi tregon fushat e shërbimit që janë të nevojshme për funksionimin e vetë kornizës, por ne kemi mundësinë të shtojmë vetitë tona shtesë në këtë zarf. Për më tepër, MassTransit ka mjete të integruara për të punuar me disa fusha opsionale, më interesante prej të cilave ne jemi të interesuar është CorrelationId.

Shtoni ndërfaqen CorrelatedBy në kontratën e mesazhit:

namespace MbMessages
{
    public interface ISomethingDoneMessageV1 : CorrelatedBy<Guid>
    {
        string Value { get; }
    }
}

Le ta zbatojmë atë dhe t'i caktojmë një vlerë pronës CorrelationId kur krijojmë një mesazh:

internal class SomethingDoneMessageV1 : ISomethingDoneMessageV1
{
    internal SomethingDoneMessageV1(Guid correlationId, string value)
    {
        CorrelationId = correlationId;
        Value = value;
    }
    
    public Guid CorrelationId { get; private set; }
    public string Value { get; private set; }
}

Nëse shikojmë mesazhin e përditësuar, do të shohim se identifikuesi i korrelacionit është bërë jo vetëm pjesë e mesazhit tonë, por edhe pjesë e zarfit - ky identifikues tani do të përdoret gjithashtu në të gjitha regjistrat MassTransit, që do të thotë se do të jetë shumë më e lehtë që ne të merremi me problemet në nivelin e ndërmjetësit të mesazheve.

{
  "messageId": "59020000-5dba-0015-10b8-08d77ec28593",
  "requestId": "59020000-5dba-0015-5674-08d77ec28592",
  "conversationId": "59020000-5dba-0015-bca8-08d77ec28594",
  "correlationId": "c7ff562a-b639-415b-9add-c9e524a727cc",
  "destinationAddress": "rabbitmq://bear.rmq.cloudamqp.com/aelzlsta/ya.servicetemplate.receiveendpoint",
  "headers": {},
  "messageType": [
    "urn:message:MbMessages:ISomethingDoneMessageV1"
  ],
  "message": {
    "correlationId": "c7ff562a-b639-415b-9add-c9e524a727cc",
    "value": "Hello"
  }
}

Na mbetet të konfigurojmë regjistrimin e këtyre vetive të shërbimit të mesazhit, për këtë ne do të shtojmë një paketë në projekt Serilog.Enrichers.MassTransitMessage. Paketa shton një filtër në tubacionin e përpunimit të mesazheve MassTransit që e shtyn kontekstin e mesazhit në një pirg të sigurt për fill. Serilogu lexon kontekstin nga grumbulli dhe shton këto veti shtesë në objektet tona të regjistrit.

Install-Package Serilog.Enrichers.MassTransitMessage

Fut një filtër në MassTransit

services
    .AddSingleton(provider =>
        {
            return Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                cfg.UseSerilog();
                cfg.UseSerilogMessagePropertiesEnricher();
            });
        });

Dhe në konfigurimin Serilog, shtoni një Enricher

Log.Logger = new LoggerConfiguration()
    .Enrich.FromMassTransitMessage()
    .CreateLogger();

Meqenëse aplikacioni që merr mesazhin nga radha RabbitMQ ka akses në të gjitha vetitë e zarfit MassTransit, ne mund të përdorim identifikuesin e korrelacionit të marrë brenda aplikacionit konsumues dhe gjithashtu ta kalojmë atë më tej përgjatë zinxhirit të thirrjes.

Si rezultat, regjistrat tanë filluan të përmbajnë CorrelationId jo vetëm brenda të njëjtit shërbim, por edhe kur ndërveprojmë me aplikacione të tjera.

Regjistrimi në një mjedis mikroservice .Net në praktikë

Pra, sistemi i regjistrimit që rezulton në aplikacionet .Net na lejon të lidhim regjistrat nga mikroshërbime krejtësisht të ndryshme pa asnjë problem - edhe ato që funksionojnë përmes një ndërmjetësi mesazhesh. Dhe me ndihmën e Elasticsearch, ne mund të analizojmë shpejt dhe me lehtësi regjistrat duke ndërtuar tabelat që na duhen në Kibana (një shembull tregohet në foto për postimin).

Sigurisht, regjistrimi në këtë formë nuk do të mbulojë opsione komplekse për ndërveprimin e shërbimeve tuaja dhe sistemeve të ndryshme të jashtme, por vendosja e një urdhri të tillë në fillimin e zhvillimit të projektit është një nga ato gjëra për të cilat do të falënderoni veten më shumë se një herë.

Ju mund të kuptoni kodin burimor të sistemit që rezulton në projekt: github.com/a-postx/YA.ServiceTemplate

Burimi: www.habr.com

Bleni një host të besueshëm për faqet me mbrojtje DDoS, serverë VPS VDS 🔥 Bleni hosting të besueshëm të faqeve të internetit me mbrojtje DDoS, servera VPS VDS | ProHoster