Seguimiento y correlación distribuidos del servicio de mensajería de Service Bus
Artículo
Uno de los problemas comunes en el desarrollo de microservicios es la capacidad de realizar un seguimiento de las operaciones de un cliente a través de todos los servicios que intervienen en el proceso. Es útil para la realizar depuraciones, análisis de rendimiento, pruebas A/B y otros escenarios de diagnóstico típicos. Una parte de este problema es el seguimiento de partes lógicas del trabajo, que incluye el resultado del procesamiento de mensajes y la latencia y las llamadas de dependencia externas. Otra parte del problema es la correlación de estos eventos de diagnóstico más allá de los límites del proceso.
Cuando un productor envía un mensaje a través de una cola, normalmente se produce en el ámbito de cualquier otra operación lógica que inició algún otro cliente o servicio. Igualmente, el consumidor continúa con la misma operación después de recibir un mensaje. Tanto el productor como el consumidor (y otros servicios que procesan la operación) emiten eventos de telemetría para hacer un seguimiento del flujo de la operación y su resultado. Para poder correlacionar estos eventos y realizar un seguimiento de la operación de un extremo a otro, cada servicio que notifica la telemetría debe marcar todos los eventos con un contexto de seguimiento.
El servicio de mensajería de Microsoft Azure Service Bus ha definido las propiedades de carga que deben usar los productores y consumidores para poder pasar estos contextos de seguimiento.
El protocolo se basa en el contexto de seguimiento de W3C.
Seguimiento automático del cliente .NET de Service Bus
La clase ServiceBusProcessor del cliente de Azure Messaging Service Bus para .NET proporciona varios puntos de instrumentación de seguimiento que se pueden enlazar mediante los sistemas de seguimiento o los fragmentos de código de cliente. La instrumentación permite realizar un seguimiento de todas las llamadas al servicio de mensajería de Service Bus desde el lado del cliente. Si el procesamiento de mensajes se realiza con ProcessMessageAsync de ServiceBusProcessor (patrón del controlador de mensajes), el procesamiento de mensajes también se instrumentará.
Realizar un seguimiento con Azure Application Insights
Microsoft Application Insights proporciona funcionalidades de supervisión de alto rendimiento entre las que se incluyen las solicitudes automágicas o el seguimiento de dependencias.
Dependiendo del tipo de proyecto, instale el SDK de Application Insights:
ASP.NET Core: instale la versión 2.2.0-beta2 o superior.
Estos vínculos proporcionan detalles sobre la instalación del SDK, la creación de recursos y la configuración del SDK (si fuera necesario). Para aplicaciones que no sean de ASP.NET, consulte el artículo Azure Application Insights for Console Applications (Azure Application Insights para las aplicaciones de consola).
Si usa ProcessMessageAsync de ServiceBusProcessor (patrón del controlador de mensajes) para procesar los mensajes, el procesamiento de mensajes también se instrumentará. Se realizará el seguimiento automático de todas las llamadas de Service Bus que se hayan hecho mediante el servicio y se correlacionarán con otros elementos de telemetría. En caso contrario, consulte el siguiente ejemplo para saber cómo realizar el seguimiento manual del procesamiento de mensajes.
Seguimiento del procesamiento de mensajes
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");
}
}
}
En este ejemplo, la telemetría de solicitudes se notifica en cada mensaje procesado e indica la marca de tiempo, la duración y el resultado (correcto). La telemetría también tiene un conjunto de propiedades de correlación. Igualmente, los seguimientos anidados y las excepciones que se notifican durante el procesamiento de mensajes también se marcan con propiedades de correlación que se representan como "elementos secundarios" de RequestTelemetry.
Si ejecuta código externo además del SDK de Application Insights, tenga previsto que la duración será mayor al ver registros de Application Insights.
Esto no significa que haya un retraso en la recepción del mensaje. En este escenario, el mensaje ya se ha recibido porque se ha pasado como un parámetro al código del SDK. Además, la etiqueta name de los registros de App Insights (Process) indica que el código de procesamiento de eventos externos está procesando el mensaje. Este problema no está relacionado con Azure. estas métricas ponen de manifiesto más bien la eficacia del código externo, dado que el mensaje ya se ha recibido de Service Bus.
Seguimiento con OpenTelemetry
La versión 7.5.0 y posteriores de la biblioteca cliente .NET de Service Bus admite OpenTelemetry en modo experimental. Para obtener más información, consulte Seguimiento distribuido en el SDK de .NET.
Realizar seguimientos sin sistema de seguimiento
Si el sistema de seguimiento no admite el seguimiento automático de llamadas de Service Bus, puede examinar la posibilidad de agregar dicha compatibilidad a un sistema de seguimiento o a su aplicación. En esta sección se describen los eventos de diagnóstico que envió el cliente .NET de Service Bus.
Activity actúa como un contexto de seguimiento mientras que DiagnosticSource es un mecanismo de notificación.
Si no hay ningún agente de escucha para los eventos DiagnosticSource, la instrumentación estará desactivada y el costo de instrumentación será cero. DiagnosticSource proporciona todo el control al agente de escucha:
El agente de escucha se encarga de controlar qué orígenes y eventos hay que escuchar.
El agente de escucha controla el muestreo y la velocidad de los eventos.
Los eventos se envían con una carga que proporciona un contexto completo para que pueda obtener acceso y modificar el objeto de mensaje durante el evento.
Familiarícese con DiagnosticSource User Guide (Guía de usuario de DiagnosticSource) antes de continuar con la implementación.
Vamos a crear un agente de escucha de eventos de Service Bus en la aplicación de ASP.NET Core que escriba registros con Microsoft.Extension.Logger.
Este usa la biblioteca System.Reactive.Core para suscribirse a DiagnosticSource (aunque también es fácil suscribirse a DiagnosticSource sin este elemento).
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();
});
}
En este ejemplo, el agente de escucha registra la duración, el resultado, el identificador único y la hora de inicio de cada operación de Service Bus.
Devolución de llamada del procesador establecida en ServiceBusProcessor. Propiedad ProcessMessageAsync
ServiceBusSessionProcessor.ProcessSessionMessage
Devolución de llamada del procesador establecida en ServiceBusSessionProcessor. Propiedad ProcessMessageAsync
Filtrado y muestreo
En algunos casos, es recomendable registrar parte de los eventos para reducir el consumo del almacenamiento o la sobrecarga del rendimiento. Para ello, puede registrar solo los eventos "Stop" (tal como se muestra en el ejemplo anterior) o un porcentaje del muestreo de los eventos.
DiagnosticSource proporciona la forma de lograrlo mediante el predicado IsEnabled. Para obtener más información, consulte Context-Based Filtering in DiagnosticSource (Filtrado basado en contexto en DiagnosticSource).
IsEnabled puede recibir varias llamadas de una sola operación para así minimizar el impacto en el rendimiento.
IsEnabled se llama en la siguiente secuencia:
IsEnabled(<OperationName>, string entity, null) por ejemplo, IsEnabled("ServiceBusSender.Send", "MyQueue1"). Tenga en cuenta que no hay ningún evento "Start" o "Stop" al final. Use esta opción para filtrar operaciones o colas determinadas. Si el método de devolución de llamada devuelve false, no se envían los eventos de la operación.
También recibirá una devolución de llamada IsEnabled(<OperationName>, string entity, Activity activity) para las operaciones "Process" y "ProcessSession". Úsela para filtrar eventos basados en activity.Id o en propiedades de etiquetas.
IsEnabled(<OperationName>.Start) por ejemplo, IsEnabled("ServiceBusSender.Send.Start"). Comprueba si se deben activar eventos "Start". El resultado solo afecta a los eventos "Start", pero la instrumentación adicional no depende de él.
No hay ningún elemento IsEnabled para el evento "Stop".
Si algún resultado de la operación es una excepción, se llama a IsEnabled("ServiceBusSender.Send.Exception"). Solo puede suscribirse a eventos "Exception" y evitar el resto de la instrumentación. En este caso, tendrá que administrar dichas excepciones. Como el resto de la instrumentación está deshabilitada, no espere que el contexto del seguimiento fluya con los mensajes de consumidor a productor.
Puede usar IsEnabled y también implementar estrategias de muestreo. Gracias al muestreo basado en Activity.Id o Activity.RootId, puede obtener resultados de muestreo coherentes en todos los elementos (siempre y cuando se propague mediante el seguimiento del sistema o nuestro propio código).
Cuando hay varios clientes de escucha DiagnosticSource en el mismo origen, un solo cliente de escucha no es suficiente para aceptar el evento, por lo que no se puede garantizar que llame a IsEnabled.
El 30 de septiembre de 2026, retiraremos las bibliotecas del SDK de Azure Service Bus WindowsAzure.ServiceBus, Microsoft.Azure.ServiceBus y com.microsoft.azure.servicebus, que no se ajustan a las directrices del SDK de Azure. También retiraremos el soporte del protocolo SBMP, por lo que ya no podrás usar este protocolo después del 30 de septiembre de 2026. Migre a las bibliotecas más recientes del SDK de Azure, que ofrecen actualizaciones de seguridad críticas y funcionalidades mejoradas, antes de esa fecha.
Aunque las bibliotecas anteriores todavía se pueden usar después del 30 de septiembre de 2026, ya no recibirán soporte técnico oficial ni actualizaciones de Microsoft. Para obtener más información, consulte el anuncio de retirada de soporte técnico.
Nombre de propiedad
Descripción
Diagnostic-Id
Identificador único de una llamada externa del productor a la cola. Vea Request-Id in HTTP protocol (Request-Id en el protocolo HTTP) para obtener el razonamiento, las consideraciones y el formato.
Correlation-Context
Contexto de la operación, que se propaga a través de todos los servicios que intervienen en el proceso de la operación. Para obtener más información, vea Correlation-Context in HTTP protocol (Correlation-Context en el protocolo HTTP).
Seguimiento automático del cliente .NET de Service Bus
A partir de la versión 3.0.0, el Cliente de Microsoft Azure ServiceBus para .NET proporciona varios puntos de instrumentación de seguimiento que se pueden enlazar mediante los sistemas de seguimiento o los fragmentos de código de cliente.
La instrumentación permite realizar un seguimiento de todas las llamadas al servicio de mensajería de Service Bus desde el lado del cliente. Si se realiza el procesamiento de mensajes con el patrón del controlador de mensajes, el procesamiento de mensajes también se instrumentará.
Realizar un seguimiento con Azure Application Insights
Microsoft Application Insights proporciona funcionalidades de supervisión de alto rendimiento entre las que se incluyen las solicitudes automágicas o el seguimiento de dependencias.
Dependiendo del tipo de proyecto, instale el SDK de Application Insights:
ASP.NET Core: instale la versión 2.2.0-beta2 o superior.
Estos vínculos proporcionan detalles sobre la instalación del SDK, la creación de recursos y la configuración del SDK (si fuera necesario). Para aplicaciones que no sean de ASP.NET, consulte el artículo Azure Application Insights for Console Applications (Azure Application Insights para las aplicaciones de consola).
Si usa un patrón de controlador de mensajes para procesar mensajes, ya ha terminado: se realizará el seguimiento automático de todas las llamadas de Service Bus que se hayan hecho mediante el servicio y se correlacionarán con otros elementos de telemetría. En caso contrario, consulte el siguiente ejemplo para saber cómo realizar el seguimiento manual del procesamiento de mensajes.
Seguimiento del procesamiento de mensajes
private readonly TelemetryClient telemetryClient;
async Task ProcessAsync(Message message)
{
var activity = message.ExtractActivity();
// 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");
}
}
En este ejemplo, RequestTelemetry se notifica en cada mensaje procesado e indica la marca de tiempo, la duración y el resultado (correcto). La telemetría también tiene un conjunto de propiedades de correlación.
Igualmente, los seguimientos anidados y las excepciones que se notifican durante el procesamiento de mensajes también se marcan con propiedades de correlación que se representan como "elementos secundarios" de RequestTelemetry.
Si ejecuta código externo además del SDK de Application Insights, tenga previsto que la duración será mayor al ver registros de Application Insights.
Esto no significa que haya un retraso en la recepción del mensaje. En este escenario, el mensaje ya se ha recibido porque se ha pasado como un parámetro al código del SDK. Además, la etiqueta name de los registros de App Insights (Process) indica que el código de procesamiento de eventos externos está procesando el mensaje. Este problema no está relacionado con Azure. estas métricas ponen de manifiesto más bien la eficacia del código externo, dado que el mensaje ya se ha recibido de Service Bus. Eche un vistazo a este archivo en GitHub para ver dónde se genera y asigna la etiqueta Process una vez que se ha recibido el mensaje de Service Bus.
Realizar seguimientos sin sistema de seguimiento
Si el sistema de seguimiento no admite el seguimiento automático de llamadas de Service Bus, puede examinar la posibilidad de agregar dicha compatibilidad a un sistema de seguimiento o a su aplicación. En esta sección se describen los eventos de diagnóstico que envió el cliente .NET de Service Bus.
Activity actúa como un contexto de seguimiento mientras que DiagnosticSource es un mecanismo de notificación.
Si no hay ningún agente de escucha para los eventos DiagnosticSource, la instrumentación estará desactivada y el costo de instrumentación será cero. DiagnosticSource proporciona todo el control al agente de escucha:
El agente de escucha se encarga de controlar qué orígenes y eventos hay que escuchar.
El agente de escucha controla el muestreo y la velocidad de los eventos.
Los eventos se envían con una carga que proporciona un contexto completo para que pueda obtener acceso y modificar el objeto de mensaje durante el evento.
Familiarícese con DiagnosticSource User Guide (Guía de usuario de DiagnosticSource) antes de continuar con la implementación.
Vamos a crear un agente de escucha de eventos de Service Bus en la aplicación de ASP.NET Core que escriba registros con Microsoft.Extension.Logger.
Este usa la biblioteca System.Reactive.Core para suscribirse a DiagnosticSource (aunque también es fácil suscribirse a DiagnosticSource sin este elemento).
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory factory, IApplicationLifetime applicationLifetime)
{
// configuration...
var serviceBusLogger = factory.CreateLogger("Microsoft.Azure.ServiceBus");
IDisposable innerSubscription = null;
IDisposable outerSubscription = DiagnosticListener.AllListeners.Subscribe(delegate (DiagnosticListener listener)
{
// subscribe to the Service Bus DiagnosticSource
if (listener.Name == "Microsoft.Azure.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;
TaskStatus status = (TaskStatus)evnt.Value.GetProperty("Status");
serviceBusLogger.LogInformation($"Operation {currentActivity.OperationName} is finished, Duration={currentActivity.Duration}, Status={status}, Id={currentActivity.Id}, StartTime={currentActivity.StartTimeUtc}");
}
});
}
});
applicationLifetime.ApplicationStopping.Register(() =>
{
outerSubscription?.Dispose();
innerSubscription?.Dispose();
});
}
En este ejemplo, el agente de escucha registra la duración, el resultado, el identificador único y la hora de inicio de cada operación de Service Bus.
Eventos
En cada operación se envían dos eventos: Start y Stop.
Lo más probablemente es que solo le interesen los eventos Stop. Estos, igual que sucede con las propiedades de actividad, proporcionan el resultado de la operación, la hora de inicio y la duración.
La carga del evento proporciona un cliente de escucha con el contexto de la operación. Replica los parámetros entrantes de la API y el valor devuelto. Asimismo, la carga del evento "Stop" tiene todas las propiedades de carga del evento "Start", por lo que puede hacer caso omiso de este último.
Todos los eventos tienen también las propiedades “Entity” y “Endpoint”.
string Entity: nombre de la entidad (cola, tema, etc.)
Uri Endpoint: dirección URL del punto de conexión de Service Bus
Cada evento "Stop" tiene la propiedad Status y la operación asincrónica TaskStatus con la que se completó, pero también se ha omitido en la tabla siguiente para simplificar el trabajo.
Esta es la lista completa de operaciones instrumentadas:
Message Message: mensaje que se está procesando. DateTimeOffset ScheduleEnqueueTimeUtc: desplazamiento de mensajes programados. long SequenceNumber: número de secuencia del mensaje programado (carga del evento "Stop").
int RequestedMessageCount: número máximo de mensajes que se pueden recibir. IList<Message> Messages: lista de mensajes recibidos (carga del evento "Stop").
int FromSequenceNumber: punto de partida desde el que se va a examinar un lote de mensajes. int RequestedMessageCount: número de mensajes que se recibirán. IList<Message> Messages: lista de mensajes recibidos (carga del evento "Stop").
IEnumerable<long> SequenceNumbers: lista que contiene los números de secuencia que se van a recibir. IList<Message> Messages: lista de mensajes recibidos (carga del evento "Stop").
string LockToken: token de bloqueo del mensaje al que renovará el bloqueo. DateTime LockedUntilUtc: nueva fecha y hora de expiración en formato UTC del token de bloqueo. (carga del evento "Stop").
string SessionId: valor de sessionId presente en los mensajes.
Microsoft.Azure.ServiceBus.Exception
cualquier API instrumentada
Exception Exception: instancia de excepción.
En todos los eventos puede obtener acceso a Activity.Current, que contiene el contexto de la operación actual.
Registro de más propiedades
Activity.Current proporciona un contexto detallado de la operación actual y sus elementos primarios. Para más información, consulte la documentación sobre las actividades.
La instrumentación de Service Bus proporciona más información en el elemento Activity.Current.Tags, ya que tiene las opciones MessageId y SessionId siempre que estén disponibles.
Las actividades que realizan el seguimiento de los eventos Receive, Peek y ReceiveDeferred también pueden tener la etiqueta RelatedTo. Esta contiene una lista distintiva de los elementos Diagnostic-Id de los mensajes que se han recibido como resultado.
Es posible que se reciban varios mensajes no relacionados debido a esta operación. Además, el elemento Diagnostic-Id no se conoce cuando se inicia la operación, por lo que las operaciones de tipo "Receive" podrían estar correlacionadas con las operaciones de tipo "Process" si se usa solo esta etiqueta. No obstante, resulta útil para analizar los problemas de rendimiento y así poder comprobar cuánto tiempo se tardó en recibir el mensaje.
Una forma eficaz de registrar las etiquetas, es realizar una iteración con ellas para que, al agregarlas al ejemplo anterior, tengan el siguiente aspecto:
Activity currentActivity = Activity.Current;
TaskStatus status = (TaskStatus)evnt.Value.GetProperty("Status");
var tagsList = new StringBuilder();
foreach (var tags in currentActivity.Tags)
{
tagsList.Append($", {tags.Key}={tags.Value}");
}
serviceBusLogger.LogInformation($"{currentActivity.OperationName} is finished, Duration={currentActivity.Duration}, Status={status}, Id={currentActivity.Id}, StartTime={currentActivity.StartTimeUtc}{tagsList}");
Filtrado y muestreo
En algunos casos, es recomendable registrar parte de los eventos para reducir el consumo del almacenamiento o la sobrecarga del rendimiento. Para ello, puede registrar solo los eventos "Stop" (tal como se muestra en el ejemplo anterior) o un porcentaje del muestreo de los eventos.
DiagnosticSource proporciona la forma de lograrlo mediante el predicado IsEnabled. Para obtener más información, consulte Context-Based Filtering in DiagnosticSource (Filtrado basado en contexto en DiagnosticSource).
IsEnabled puede recibir varias llamadas de una sola operación para así minimizar el impacto en el rendimiento.
IsEnabled se llama en la siguiente secuencia:
IsEnabled(<OperationName>, string entity, null) por ejemplo, IsEnabled("Microsoft.Azure.ServiceBus.Send", "MyQueue1"). Tenga en cuenta que no hay ningún evento "Start" o "Stop" al final. Use esta opción para filtrar operaciones o colas determinadas. Si el método de devolución de llamada devuelve false, no se envían los eventos de la operación.
También recibirá una devolución de llamada IsEnabled(<OperationName>, string entity, Activity activity) para las operaciones "Process" y "ProcessSession". Úsela para filtrar eventos basados en activity.Id o en propiedades de etiquetas.
IsEnabled(<OperationName>.Start) por ejemplo, IsEnabled("Microsoft.Azure.ServiceBus.Send.Start"). Comprueba si se deben activar eventos "Start". El resultado solo afecta a los eventos "Start", pero la instrumentación adicional no depende de él.
No hay ningún elemento IsEnabled para el evento "Stop".
Si algún resultado de la operación es una excepción, se llama a IsEnabled("Microsoft.Azure.ServiceBus.Exception"). Solo puede suscribirse a eventos "Exception" y evitar el resto de la instrumentación. En este caso, tendrá que administrar dichas excepciones. Como el resto de la instrumentación está deshabilitada, no espere que el contexto del seguimiento fluya con los mensajes de consumidor a productor.
Puede usar IsEnabled y también implementar estrategias de muestreo. Gracias al muestreo basado en Activity.Id o Activity.RootId, puede obtener resultados de muestreo coherentes en todos los elementos (siempre y cuando se propague mediante el seguimiento del sistema o nuestro propio código).
Cuando hay varios clientes de escucha DiagnosticSource en el mismo origen, un solo cliente de escucha no es suficiente para aceptar el evento, por lo que no se puede garantizar que llame a IsEnabled.
Contenido relacionado
Una biblioteca que puede ayudar a los desarrolladores a emitir todos estos datos de telemetría de manera predeterminada es NServiceBus.