Distribuerad spårning och korrelation via Service Bus-meddelanden

Ett av de vanligaste problemen inom utveckling av mikrotjänster är möjligheten att spåra åtgärder från en klient via alla tjänster som ingår i bearbetningen. Det är användbart för felsökning, prestandaanalys, A/B-testning och andra typiska diagnostikscenarier. En del av det här problemet är att spåra logiska delar av arbetet. Den innehåller resultat för meddelandebearbetning, svarstid och externa beroendeanrop. En annan del är korrelationen av dessa diagnostikhändelser utanför processgränserna.

När en producent skickar ett meddelande via en kö sker det vanligtvis i omfånget för någon annan logisk åtgärd som initieras av någon annan klient eller tjänst. Samma åtgärd fortsätter av konsumenten när den får ett meddelande. Både producent och konsument (och andra tjänster som bearbetar åtgärden) genererar förmodligen telemetrihändelser för att spåra åtgärdsflödet och resultatet. För att korrelera sådana händelser och spårningsåtgärder från slutpunkt till slutpunkt måste varje tjänst som rapporterar telemetri stämpla varje händelse med en spårningskontext. Ett bibliotek som kan hjälpa utvecklare att få all denna telemetri genererad som standard är NServiceBus.

Microsoft Azure Service Bus-meddelanden har definierat nyttolastegenskaper som producenter och konsumenter bör använda för att skicka en sådan spårningskontext. Protokollet baseras på W3C Trace-Context.

Egenskapsnamn beskrivning
Diagnostik-ID Unik identifierare för ett externt anrop från producent till kön. Se W3C Trace-Context traceparent-huvudet för formatet

Autospårning av Service Bus .NET-klient

Klassen ServiceBusProcessor för Azure Messaging Service Bus-klienten för .NET tillhandahåller spårningsinstrumentationspunkter som kan kopplas via spårningssystem eller klientkod. Instrumentationen tillåter spårning av alla anrop till Service Bus-meddelandetjänsten från klientsidan. Om meddelandebearbetningen utförs med hjälp ProcessMessageAsync av ServiceBusProcessor (mönstret för meddelandehanteraren) instrumenteras även meddelandebearbetningen.

Spårning med Azure Application Insights

Microsoft Application Insights innehåller omfattande funktioner för prestandaövervakning, inklusive automagisk begäran och beroendespårning.

Beroende på projekttyp installerar du Application Insights SDK:

Om du använder ProcessMessageAsync ServiceBusProcessor (mönster för meddelandehanterare) för att bearbeta meddelanden, instrumenteras även meddelandebearbetningen. Alla Service Bus-anrop som utförs av din tjänst spåras automatiskt och korreleras med andra telemetriobjekt. I annat fall kan du läsa följande exempel för manuell spårning av meddelandebearbetning.

Bearbetning av spårningsmeddelanden

async Task ProcessAsync(ProcessMessageEventArgs args)
{
    ServiceBusReceivedMessage message = args.Message;
    if (message.ApplicationProperties.TryGetValue("Diagnostic-Id", out var objectId) && objectId is string diagnosticId)
    {
        var activity = new Activity("ServiceBusProcessor.ProcessMessage");
        activity.SetParentId(diagnosticId);
        // If you're using Microsoft.ApplicationInsights package version 2.6-beta or higher, you should call StartOperation<RequestTelemetry>(activity) instead
        using (var operation = telemetryClient.StartOperation<RequestTelemetry>("Process", activity.RootId, activity.ParentId))
        {
            telemetryClient.TrackTrace("Received message");
            try 
            {
            // process message
            }
            catch (Exception ex)
            {
                telemetryClient.TrackException(ex);
                operation.Telemetry.Success = false;
                throw;
            }

            telemetryClient.TrackTrace("Done");
        }
    }
}

I det här exemplet rapporteras telemetri för begäran för varje bearbetat meddelande med tidsstämpel, varaktighet och resultat (lyckades). Telemetrin har också en uppsättning korrelationsegenskaper. Kapslade spårningar och undantag som rapporteras under meddelandebearbetningen stämplas också med korrelationsegenskaper som representerar dem som "underordnade" för RequestTelemetry.

Om du anropar externa komponenter som stöds under meddelandebearbetningen spåras och korreleras de också automatiskt. Se Spåra anpassade åtgärder med Application Insights .NET SDK för manuell spårning och korrelation.

Om du kör någon extern kod utöver Application Insights SDK kan du förvänta dig att se längre varaktighet när du visar Application Insights-loggar.

Längre varaktighet i Application Insights-loggen

Det betyder inte att det uppstod en fördröjning i mottagandet av meddelandet. I det här scenariot har meddelandet redan tagits emot eftersom meddelandet skickas som en parameter till SDK-koden. Och namntaggen i App Insights-loggarna (Process) anger att meddelandet nu bearbetas av din externa kod för händelsebearbetning. Det här problemet är inte Azure-relaterat. I stället refererar dessa mått till effektiviteten i din externa kod eftersom meddelandet redan har tagits emot från Service Bus.

Spårning med OpenTelemetry

Service Bus .NET-klientbibliotek version 7.5.0 och senare stöder OpenTelemetry i experimentellt läge. Mer information finns i Distribuerad spårning i .NET SDK.

Spårning utan spårningssystem

Om spårningssystemet inte stöder automatisk Service Bus-anropsspårning kanske du tittar på att lägga till sådan support i ett spårningssystem eller i ditt program. I det här avsnittet beskrivs diagnostikhändelser som skickas av Service Bus .NET-klienten.

Service Bus .NET-klienten är instrumenterad med hjälp av .NET-spårningsprimiterna System.Diagnostics.Activity och System.Diagnostics.DiagnosticSource.

Activity fungerar som en spårningskontext medan DiagnosticSource det är en meddelandemekanism.

Om det inte finns någon lyssnare för DiagnosticSource-händelserna är instrumentationen avstängd, vilket behåller noll instrumentationskostnader. DiagnosticSource ger lyssnaren all kontroll:

  • lyssnaren styr vilka källor och händelser som ska lyssnas på
  • lyssnaren styr händelsefrekvens och sampling
  • händelser skickas med en nyttolast som ger fullständig kontext så att du kan komma åt och ändra meddelandeobjektet under händelsen

Bekanta dig med DiagnosticSource-användarhandboken innan du fortsätter med implementeringen.

Nu ska vi skapa en lyssnare för Service Bus-händelser i ASP.NET Core-appen som skriver loggar med Microsoft.Extension.Logger. Det använder System.Reactive.Core-biblioteket för att prenumerera på DiagnosticSource (det är också enkelt att prenumerera på DiagnosticSource utan det)

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory factory, IApplicationLifetime applicationLifetime)
{
    // configuration...

    var serviceBusLogger = factory.CreateLogger("Azure.Messaging.ServiceBus");

    IDisposable innerSubscription = null;
    IDisposable outerSubscription = DiagnosticListener.AllListeners.Subscribe(delegate (DiagnosticListener listener)
    {
        // subscribe to the Service Bus DiagnosticSource
        if (listener.Name == "Azure.Messaging.ServiceBus")
        {
            // receive event from Service Bus DiagnosticSource
            innerSubscription = listener.Subscribe(delegate (KeyValuePair<string, object> evnt)
            {
                // Log operation details once it's done
                if (evnt.Key.EndsWith("Stop"))
                {
                    Activity currentActivity = Activity.Current;
                    serviceBusLogger.LogInformation($"Operation {currentActivity.OperationName} is finished, Duration={currentActivity.Duration}, Id={currentActivity.Id}, StartTime={currentActivity.StartTimeUtc}");
                }
            });
        }
    });

    applicationLifetime.ApplicationStopping.Register(() =>
    {
        outerSubscription?.Dispose();
        innerSubscription?.Dispose();
    });
}

I det här exemplet loggar lyssnaren varaktighet, resultat, unik identifierare och starttid för varje Service Bus-åtgärd.

Händelser

Alla händelser har följande egenskaper som överensstämmer med den öppna telemetrispecifikationen: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md.

  • message_bus.destination – kö/ämne/prenumerationssökväg
  • peer.address – fullständigt kvalificerat namnområde
  • kind – antingen producent, konsument eller kund. Producenten används när du skickar meddelanden, konsumenter när de tar emot och klienten när de etableras.
  • componentservicebus

Alla händelser har Entity också och Endpoint egenskaper.

  • Entity – Namnet på entiteten (kö, ämne och så vidare.)
  • Endpoint – Service Bus-slutpunkts-URL

Instrumenterade åtgärder

Här är den fullständiga listan över instrumenterade åtgärder:

Åtgärdsnamn Spårat API
ServiceBusSender.Send ServiceBusSender.SendMessageAsync
ServiceBusSender.SendMessagesAsync
ServiceBusSender.Schedule ServiceBusSender.ScheduleMessageAsync
ServiceBusSender.ScheduleMessagesAsync
ServiceBusSender.Cancel ServiceBusSender.CancelScheduledMessageAsync
ServiceBusSender.CancelScheduledMessagesAsync
ServiceBusReceiver.Receive ServiceBusReceiver.ReceiveMessageAsync
ServiceBusReceiver.ReceiveMessagesAsync
ServiceBusReceiver.ReceiveDeferred ServiceBusReceiver.ReceiveDeferredMessagesAsync
ServiceBusReceiver.Peek ServiceBusReceiver.PeekMessageAsync
ServiceBusReceiver.PeekMessagesAsync
ServiceBusReceiver.Abandon ServiceBusReceiver.AbandonMessagesAsync
ServiceBusReceiver.Complete ServiceBusReceiver.CompleteMessagesAsync
ServiceBusReceiver.DeadLetter ServiceBusReceiver.DeadLetterMessagesAsync
ServiceBusReceiver.Defer ServiceBusReceiver.DeferMessagesAsync
ServiceBusReceiver.RenewMessageLock ServiceBusReceiver.RenewMessageLockAsync
ServiceBusSessionReceiver.RenewSessionLock ServiceBusSessionReceiver.RenewSessionLockAsync
ServiceBusSessionReceiver.GetSessionState ServiceBusSessionReceiver.GetSessionStateAsync
ServiceBusSessionReceiver.SetSessionState ServiceBusSessionReceiver.SetSessionStateAsync
ServiceBusProcessor.ProcessMessage Processoråteranrop inställt på ServiceBusProcessor. Egenskapen ProcessMessageAsync
ServiceBusSessionProcessor.ProcessSessionMessage Processoråteranrop inställt på ServiceBusSessionProcessor. Egenskapen ProcessMessageAsync

Filtrering och sampling

I vissa fall är det önskvärt att bara logga en del av händelserna för att minska prestandakostnaderna eller lagringsförbrukningen. Du kan bara logga "Stoppa"-händelser (som i föregående exempel) eller exempelprocent av händelserna. DiagnosticSource ge ett sätt att uppnå det med IsEnabled predikat. Mer information finns i Sammanhangsbaserad filtrering i DiagnosticSource.

IsEnabled kan anropas flera gånger för en enda åtgärd för att minimera prestandapåverkan.

IsEnabled anropas i följande sekvens:

  1. IsEnabled(<OperationName>, string entity, null) till exempel IsEnabled("ServiceBusSender.Send", "MyQueue1"). Observera att det inte finns någon "Start" eller "Stop" i slutet. Använd den för att filtrera bort vissa åtgärder eller köer. Om motringningsmetoden returnerar falseskickas inte händelser för åtgärden.

    • För åtgärderna Process och ProcessSession får IsEnabled(<OperationName>, string entity, Activity activity) du även återanrop. Använd den för att filtrera händelser baserat på activity.Id eller Egenskaper för taggar.
  2. IsEnabled(<OperationName>.Start) till exempel IsEnabled("ServiceBusSender.Send.Start"). Kontrollerar om starthändelsen ska utlösas. Resultatet påverkar bara "Start"-händelsen, men ytterligare instrumentation är inte beroende av den.

Det finns inget IsEnabled för "Stop"-händelsen.

Om något åtgärdsresultat är ett undantag IsEnabled("ServiceBusSender.Send.Exception") anropas. Du kunde bara prenumerera på undantagshändelser och förhindra resten av instrumentationen. I det här fallet måste du fortfarande hantera sådana undantag. Eftersom annan instrumentation är inaktiverad bör du inte förvänta dig att spårningskontexten flödar med meddelandena från konsument till producent.

Du kan också använda IsEnabled implementeringsstrategier för sampling. Sampling baserat på Activity.Id eller Activity.RootId säkerställer konsekvent sampling över alla däck (så länge det sprids genom spårningssystem eller av din egen kod).

I närvaro av flera DiagnosticSource lyssnare för samma källa räcker det för bara en lyssnare att acceptera händelsen, så det finns ingen garanti som IsEnabled anropas.

Nästa steg