Correlazione e analisi distribuita tramite la messaggistica del bus di servizio

Uno dei problemi comuni nello sviluppo di micro servizi è la possibilità di tracciare l'operazione da un client attraverso tutti i servizi coinvolti nell'elaborazione. Si tratta di una caratteristica utile per il debug, l'analisi delle prestazioni, i test A/B e altri comuni scenari di diagnostica. Una parte di questo problema consiste nel tenere traccia degli elementi di lavoro logici. Tale operazione include i risultati e la latenza dell'elaborazione dei messaggi e le chiamate di dipendenza esterne. Un'altra parte è costituita dalla correlazione di questi eventi di diagnostica oltre i limiti del processo.

Quando un producer invia un messaggio tramite una coda, tale operazione avviene in genere nell'ambito di un'altra operazione logica, avviata da un altro client o servizio. La stessa operazione viene portata avanti dal consumer dopo che ha ricevuto un messaggio. Sia il producer che il consumer (e altri servizi che elaborano l'operazione) generano presumibilmente eventi di telemetria per analizzare il flusso e il risultato dell'operazione. Per correlare tali eventi e l'operazione di analisi end-to-end, ogni servizio che restituisce dati di telemetria deve contrassegnare tutti gli eventi con un contesto di analisi.

La messaggistica del bus di servizio di Microsoft Azure include proprietà di payload definite che producer e consumer devono usare per passare tale contesto di analisi. Il protocollo si basa sul contesto di traccia W3C.

Nome proprietà Descrizione
Diagnostic-Id Identificatore univoco di una chiamata esterna alla coda effettuata dal producer. Fare riferimento all'intestazione traceparent del contesto di traccia W3C per il formato

bus di servizio la traccia automatica del client .NET

La ServiceBusProcessor classe del client di Messaggistica di Azure bus di servizio per .NET fornisce punti di strumentazione di traccia che possono essere agganciati da sistemi di traccia o da parti di codice client. La strumentazione consente di verificare tutte le chiamate al servizio di messaggistica del bus di servizio dal lato client. Se l'elaborazione dei messaggi viene eseguita utilizzando ProcessMessageAsyncServiceBusProcessor (modello di gestore messaggi), viene instrumentata anche l'elaborazione dei messaggi.

Verifica con Azure Application Insights

Microsoft Application Insights offre funzionalità avanzate di monitoraggio delle prestazioni, tra cui la verifica automatica delle richieste e delle dipendenze.

A seconda del tipo di progetto, installare Application Insights SDK:

  • ASP.NET: installare la versione 2.5-beta2 o una versione successiva
  • ASP.NET Core: installare la versione 2.2.0-beta2 o una versione successiva. Questi collegamenti forniscono informazioni dettagliate su come installare l'SDK, creare risorse e, se necessario, configurare l'SDK. Per le applicazioni non ASP.NET, vedere l'articolo Azure Application Insights for Console Applications (Azure Application Insights per applicazioni console).

Se si usa ProcessMessageAsync (modello di ServiceBusProcessor gestore messaggi) per elaborare i messaggi, viene instrumentata anche l'elaborazione dei messaggi. Tutte le chiamate bus di servizio eseguite dal servizio vengono rilevate automaticamente e correlate ad altri elementi di telemetria. In caso contrario vedere l'esempio seguente per la verifica manuale delle operazioni di elaborazione dei messaggi.

Analizzare l'elaborazione dei messaggi

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");
        }
    }
}

In questo esempio, i dati di telemetria delle richieste vengono segnalati per ogni messaggio elaborato, con un timestamp, una durata e un risultato (esito positivo). La telemetria include anche un set di proprietà di correlazione. Alle eccezioni e alle analisi nidificate restituite durante l'elaborazione dei messaggi vengono inoltre assegnate proprietà di correlazione in modo che vengano rappresentate come elementi figlio di RequestTelemetry.

Se si effettuano chiamate a componenti esterni supportati durante l'elaborazione dei messaggi, vengono rilevati e correlati automaticamente. Per informazioni sulla verifica e la correlazione manuale, vedere Verifica delle operazioni personalizzate con Application Insights .NET SDK.

Se si esegue codice esterno oltre all'SDK di Application Insights, si prevede di visualizzare una durata più lunga quando si visualizzano i log di Application Insights.

Longer duration in Application Insights log

Non significa che si sia verificato un ritardo nella ricezione del messaggio. In questo scenario, il messaggio è già stato ricevuto perché il messaggio viene passato come parametro al codice DELL'SDK. Il tag del nome nei log di App Insights (Processo) indica inoltre che il messaggio viene ora elaborato dal codice di elaborazione eventi esterno. Questo problema non è correlato ad Azure. Queste metriche fanno invece riferimento all'efficienza del codice esterno, dato che il messaggio è già stato ricevuto da bus di servizio.

Rilevamento con OpenTelemetry

bus di servizio libreria client .NET versione 7.5.0 e successive supporta OpenTelemetry in modalità sperimentale. Per altre informazioni, vedere Traccia distribuita in .NET SDK.

Verifica senza sistema di analisi

Nel caso in cui il sistema di traccia non supporti il rilevamento automatico delle chiamate bus di servizio potrebbe essere necessario aggiungere tale supporto in un sistema di traccia o nell'applicazione. Questa sezione illustra gli eventi di diagnostica inviati dal client .NET del bus di servizio.

Per instrumentare il client .NET del bus di servizio, si usano le primitive di analisi .NET System.Diagnostics.Activity e System.Diagnostics.DiagnosticSource.

Activity funge da un contesto di analisi mentre DiagnosticSource è un meccanismo di notifica.

Se non è presente alcun listener per gli eventi DiagnosticSource, la strumentazione è disattivata, mantenendo zero costi di strumentazione. DiagnosticSource cede il controllo completo al listener:

  • il listener controlla le origini e gli eventi di cui rimanere in ascolto
  • il listener controlla il campionamento e la frequenza degli eventi
  • gli eventi vengono inviati con un payload che fornisce il contesto completo, in modo che sia possibile accedere e modificare l'oggetto Message durante l'evento

Prima di procedere con l'implementazione, è consigliabile leggere il manuale dell'utente di DiagnosticSource.

Nel codice seguente verrà creato un listener per eventi del bus di servizio nell'app ASP.NET Core che scrive i log con Microsoft.Extension.Logger. Il listener usa la libreria System.Reactive.Core per eseguire la sottoscrizione a DiagnosticSource. È comunque possibile eseguire facilmente la sottoscrizione a DiagnosticSource anche senza la libreria.

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();
    });
}

In questo esempio il listener registra durata, risultato, identificatore univoco e ora di inizio di ogni operazione del bus di servizio.

evento

Tutti gli eventi avranno le proprietà seguenti conformi alla specifica di telemetria aperta: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md.

  • message_bus.destination : percorso coda/argomento/sottoscrizione
  • peer.address - spazio dei nomi completo
  • kind : producer, consumer o client. Il producer viene usato durante l'invio di messaggi, consumer durante la ricezione e il client durante la risoluzione.
  • componentservicebus

Tutti gli eventi hanno Entity anche proprietà e Endpoint .

  • Entity - - Nome dell'entità (coda, argomento e così via).
  • Endpoint: URL dell'endpoint del bus di servizio

Operazioni instrumentate

Ecco l'elenco completo delle operazioni instrumentate:

Nome operazione API verificata
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 Callback del processore impostato su ServiceBusProcessor. ProcessMessageAsync - proprietà
ServiceBusSessionProcessor.ProcessSessionMessage Callback del processore impostato su ServiceBusSessionProcessor. ProcessMessageAsync - proprietà

Filtro e campionamento

In alcuni casi è consigliabile registrare solo parte degli eventi per ridurre il consumo dello spazio di archiviazione o il sovraccarico delle prestazioni. È possibile registrare solo gli eventi di tipo 'Stop' (come nell'esempio precedente) o una percentuale campione degli eventi. A questo scopo, è possibile usare DiagnosticSource con il predicato IsEnabled. Per altre informazioni, vedere Context-Based Filtering in DiagnosticSource (Filtro in base al contesto in DiagnosticSource).

Per ridurre al minimo l'impatto sulle prestazioni, è possibile chiamare IsEnabled più volte per una singola operazione.

IsEnabled viene chiamato nella sequenza seguente:

  1. IsEnabled(<OperationName>, string entity, null) ad esempio, IsEnabled("ServiceBusSender.Send", "MyQueue1"). Si noti che non c'è "Start" o "Stop" alla fine. Usarlo per escludere determinate operazioni o code. Se il metodo di callback restituisce false, gli eventi per l'operazione non vengono inviati.

    • Per le operazioni di tipo 'Process' e 'ProcessSession' viene ricevuto anche il callback IsEnabled(<OperationName>, string entity, Activity activity). Usarlo per filtrare gli eventi in base alle proprietà activity.Id o Tags.
  2. IsEnabled(<OperationName>.Start) ad esempio, IsEnabled("ServiceBusSender.Send.Start"). Controlla se deve essere attivato l'evento 'Start'. Il risultato influisce solo sull'evento "Start", ma non dipende da altri strumenti.

Non esiste alcun IsEnabled evento "Stop".

Se il risultato di alcune operazioni è un'eccezione, viene chiamato IsEnabled("ServiceBusSender.Send.Exception"). È possibile eseguire la sottoscrizione solo di eventi 'Exception' e impedire il resto della strumentazione. In questo caso sarà comunque necessario gestire tali eccezioni. Poiché altre strumentazioni sono disabilitate, non è consigliabile prevedere il flusso del contesto di traccia con i messaggi dal consumer al producer.

È possibile usare IsEnabled anche per implementare strategie di campionamento. Il campionamento basato su Activity.Id o Activity.RootId garantisce un campionamento coerente tra tutti i pneumatici (purché venga propagato dal sistema di traccia o dal proprio codice).

In presenza di più DiagnosticSource listener per la stessa origine, è sufficiente che un solo listener accetti l'evento, quindi non esiste alcuna garanzia che IsEnabled venga chiamata.

Passaggi successivi