Rastreamento distribuído e correlação por meio de mensagens do Barramento de Serviço

Um dos problemas comuns no desenvolvimento de microsserviços é a capacidade de rastrear a operação de um cliente em todos os serviços que estão envolvidos no processamento. É útil para depuração, análise de desempenho, testes A/B e outros cenários típicos de diagnóstico. Uma parte desse problema é monitorar partes lógicas do trabalho. Inclui o resultado e latência de processamento de mensagem e chamadas de dependência externa. Outra parte é correlação desses eventos de diagnóstico além dos limites do processo.

Quando um produtor envia uma mensagem por meio de uma fila, normalmente acontece no escopo de alguma outra operação lógica, iniciado por algum outro cliente ou serviço. A mesma operação dará continuidade ao consumidor quando ele recebe uma mensagem. O produtor e o consumidor (e outros serviços que processam a operação), presumivelmente emitem eventos de telemetria para rastrear o fluxo e o resultado da operação. Para correlacionar esses eventos e rastrear a operação end-to-end, cada serviço que informa a telemetria deve gravar todos os eventos com um contexto de rastreamento.

Mensagens do Barramento de Serviço do Microsoft Azure definiu propriedades de carga que produtores e consumidores devem usar para passar esse contexto de rastreamento. O protocolo é baseado no contexto de rastreamento W3C.

Nome da propriedade Descrição
ID de diagnóstico Identificador exclusivo de uma chamada externa do produtor para a fila. Consulte W3C Trace-Context traceparent header para o formato

Rastreamento automático do cliente .NET do Barramento de Serviço

A classe ServiceBusProcessor do cliente do Barramento de Serviço de Mensagens do Azure para .NET fornece pontos de instrumentação de rastreamento que podem ser conectados por sistemas de rastreamento ou por parte do código de cliente. A instrumentação permite controlar todas as chamadas para o serviço de mensagens do Barramento de Serviço do lado do cliente. Se o processamento de mensagens for feito com o ProcessMessageAsync do ServiceBusProcessor (padrão do manipulador de mensagens), ele também será instrumentado.

Rastreando o Application Insights do Azure

O Microsoft Application Insights fornece recursos de monitoramento de desempenho avançado, incluindo solicitações automáticas e rastreamento de dependências.

Dependendo do tipo do seu projeto, instale o SDK do Application Insights:

Se você usar o ProcessMessageAsync do ServiceBusProcessor (padrão do manipulador de mensagens) para processar mensagens, o processamento de mensagens também será instrumentado. Todas as chamadas do Barramento de Serviço feitas pelo seu serviço são automaticamente rastreadas e correlacionadas com outros itens de telemetria. Caso contrário, consulte o exemplo a seguir para o controle de processamento de mensagem manual.

Processamento de mensagem de rastreamento

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

Neste exemplo, é relatada a telemetria da solicitação de cada mensagem processada, com um carimbo de data/hora, a duração e o resultado (êxito). A telemetria também tem um conjunto de propriedades de correlação. Rastreamentos aninhados e exceções relatadas durante o processamento da mensagem também são marcadas com propriedades de correlação que os representam como filhos do RequestTelemetry.

Caso faça chamadas para componentes externos com suporte durante o processamento da mensagem, elas também serão automaticamente rastreadas e correlacionadas. Consulte Acompanhar operações personalizadas com o SDK do .NET do Application Insights para rastreamento manual e correlação.

Se você estiver executando qualquer código externo além do SDK do Application Insights, espere para ver uma duração mais longa ao exibir os logs de Application Insights.

Longer duration in Application Insights log

Isso não significa que houve um atraso no recebimento da mensagem. Nesse cenário, a mensagem já foi recebida, pois a ela é passada como um parâmetro para o código do SDK. E a marca name nos logs do App Insights (Processo) indica que agora a mensagem está sendo processada pelo código de processamento de evento externo. Esse problema não é relacionado ao Azure. Em vez disso, essas métricas referem-se à eficiência do seu código externo, dado que a mensagem já foi recebida do Barramento de Serviço.

Acompanhamento com OpenTelemetry

A biblioteca de clientes .NET de Barramento de Serviço versão 7.5.0 e posterior dá suporte a OpenTelemetry no modo experimental. Para obter mais informações, confira Rastreamento distribuído no SDK do .NET.

Controle sem o sistema de rastreamento

Caso o seu sistema de rastreamento não dê suporte ao acompanhamento de chamadas automático do Barramento de Serviço, considere adicionar esse suporte a um sistema de acompanhamento ou ao seu aplicativo. Esta seção descreve eventos de diagnóstico enviados pelo cliente .NET do Barramento de Serviço.

Cliente de .NET do Barramento de Serviço é instrumentado usando primitivos de rastreamento .NET System.Diagnostics.Activity e System.Diagnostics.DiagnosticSource.

Activity serve como um contexto de rastreamento enquanto DiagnosticSource é um mecanismo de notificação.

Se não houver nenhum ouvinte para os eventos do DiagnosticSource, a instrumentação será desativada, mantendo o custo zero da instrumentação. O DiagnosticSource fornece todo o controle para o ouvinte:

  • o ouvinte controla quais fontes e eventos escutar
  • o ouvinte controla a amostragem e taxa do evento
  • os eventos são enviados com uma carga que fornece o contexto completo para que você possa acessar e modificar o objeto de mensagem durante o evento

Familiarize-se com Guia do Usuário DiagnosticSource antes de continuar com a implementação.

Vamos criar um ouvinte de eventos do Barramento de Serviço no aplicativo do ASP.NET Core que grava logs com o Microsoft.Extension.Logger. Ele usa a biblioteca System.Reactive.Core para assinar DiagnosticSource (também é fácil assinar DiagnosticSource sem ela)

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

Neste exemplo, o ouvinte registra duração, resultado, identificador exclusivo e a hora de início para cada operação de Barramento de Serviço.

Eventos

Todos os eventos terão as seguintes propriedades que estão em conformidade com a especificação de telemetria aberta: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md.

  • message_bus.destination – fila/tópico/caminho de assinatura
  • peer.address – namespace totalmente qualificado
  • kind – produtor, consumidor ou cliente. O produtor é usado ao enviar mensagens, o consumidor é usado para recebê-las e o cliente é usado para resolvê-las.
  • componentservicebus

Todos os eventos também têm Entity e propriedades Endpoint.

  • Entity - - Nome da entidade (fila, tópico etc.)
  • Endpoint - URL do endpoint do Barramento de Serviço

Operações instrumentadas

Veja abaixo a lista completa de operações instrumentadas:

Nome de operação API controlada
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 Retorno de chamada do processador definido em ServiceBusProcessor. Propriedade ProcessMessageAsync
ServiceBusSessionProcessor.ProcessSessionMessage Retorno de chamada do processador definido em ServiceBusSessionProcessor. Propriedade ProcessMessageAsync

Filtragem e amostragem

Em alguns casos, é desejável para fazer logon apenas uma parte dos eventos para reduzir o consumo de armazenamento ou sobrecarga de desempenho. Você pode fazer somente eventos de 'Stop' (como no exemplo anterior) ou a porcentagem de exemplo dos eventos. DiagnosticSource fornecem a maneira de conseguir isso com IsEnabled predicado. Para obter mais informações, consulte Filtragem com base em contexto em DiagnosticSource.

IsEnabled pode ser chamado várias vezes para uma única operação para minimizar o impacto de desempenho.

IsEnabled é chamado na sequência a seguir:

  1. IsEnabled(<OperationName>, string entity, null) por exemplo, IsEnabled("ServiceBusSender.Send", "MyQueue1"). Observe que não há nenhum 'Start' ou 'Stop' no final. Usá-lo para filtrar as operações específicas ou filas. Se o método de retorno de chamada retornar false, os eventos da operação não serão enviados.

    • Para as operações de 'Processo' e 'ProcessSession', você também receberá IsEnabled(<OperationName>, string entity, Activity activity) retorno de chamada. Use-o para filtrar eventos com base em activity.Id ou propriedades de marcas.
  2. IsEnabled(<OperationName>.Start) por exemplo, IsEnabled("ServiceBusSender.Send.Start"). Verifica se o evento 'Start' deva ser acionado. O resultado afeta apenas o evento 'Start', mas instrumentações adicionais não dependem disso.

Não há nenhum IsEnabled para o evento 'Stop'.

Se algum resultado da operação for a exceção, IsEnabled("ServiceBusSender.Send.Exception") é chamado. Você só pode assinar eventos de 'Exceções' e impedir o restante da instrumentação. Nesse caso, você ainda precisa tratar essas exceções. Como outras instrumentações estão desabilitadas, você não deve esperar que o contexto de rastreamento exiba as mensagens do consumidor para o produtor.

Você pode usar IsEnabled também implementar estratégias de amostragem. Amostragem com base no Activity.Id ou no Activity.RootId garante uma amostragem consistente em todas as camadas (desde que ela seja propagada pelo sistema de rastreamento ou pelo seu código).

Na presença de vários ouvintes DiagnosticSource para a mesma fonte, apenas um ouvinte precisa aceitar o evento, portanto não há garantia que o IsEnabled será chamado.

Próximas etapas