Suivi distribué et corrélation via la messagerie Service Bus
L’un des défis que pose couramment le développement de microservices est la capacité à tracer l’opération d’un client à travers tous les services impliqués dans le traitement. Le traçage facilite le débogage, l’analyse des performances, les tests A/B et d’autres diagnostics standard. L’un des aspects du problème a trait au suivi d’éléments de travail logiques. Ceux-ci comptent notamment le résultat et la latence du traitement des messages, et les appels de dépendances externes. Un autre aspect du problème est la mise en corrélation de ces événements de diagnostic au-delà des limites de traitement.
Quand un producteur envoie un message par le biais d’une file d’attente, cela se produit généralement dans l’étendue d’une autre opération logique lancée par un autre client ou service. Le consommateur continue la même opération quand il reçoit un message. Le producteur et le consommateur (et les autres services qui traitent l’opération) émettent probablement des événements de télémétrie pour tracer le flux et le résultat de l’opération. Pour mettre en corrélation de tels événements et tracer l’opération de bout en bout, chaque service qui signale des données de télémétrie doit horodater les événements avec un contexte de trace. NServiceBus est une bibliothèque qui peut aider les développeurs à avoir toutes ces données de télémétrie émises par défaut.
La messagerie Microsoft Azure Service Bus définit des propriétés de charge utile que les producteurs et les consommateurs doivent utiliser pour passer ce contexte de trace. Le protocole est basé sur la spécification Trace-Context de W3C.
- Kit de développement logiciel (SDK) Azure.Messaging.ServiceBus (Dernier)
- Kit de développement logiciel (SDK) Microsoft.Azure.ServiceBus
Nom de la propriété | Description |
---|---|
Diagnostic-Id | Identificateur unique d’un appel externe du producteur à la file d’attente. Reportez-vous à l’en-tête traceparent de la spécification Trace-Context de W3C pour le format. |
Traçage automatique du client .NET Service Bus
La classe ServiceBusProcessor
du client Service Bus d’Azure Messaging pour .NET fournit des points d’instrumentation de traçage qui peuvent être interceptés par des systèmes de traçage ou des éléments de code client. L’instrumentation permet le suivi de tous les appels au service de messagerie Service Bus du côté client. Si le traitement des messages est effectué avec ProcessMessageAsync
de ServiceBusProcessor
(modèle de gestionnaire de messages), le traitement des messages est également instrumenté.
Suivi avec Azure Application Insights
Microsoft Application Insights fournit des fonctionnalités riches de monitoring des performances, notamment le suivi automagique des requêtes et des dépendances.
En fonction de votre type de projet, installez le SDK Application Insights :
- ASP.NET : installez la version 2.5 (bêta 2) ou ultérieure.
- ASP.NET Core : installez la version 2.2.0 (bêta 2) ou ultérieure. Ces liens fournissent des détails sur l’installation du SDK, la création de ressources et la configuration du SDK (le cas échéant). Pour les applications non-ASP.NET, consultez l’article Azure Application Insights pour les applications console.
Si vous utilisez ProcessMessageAsync
de ServiceBusProcessor
(modèle de gestionnaire de messages) pour traiter les messages, le traitement des messages est également instrumenté. Tous les appels Service Bus effectués par votre service sont automatiquement suivis et mis en corrélation avec d’autres éléments de télémétrie. Sinon, passez en revue l’exemple suivant qui illustre le suivi manuel du traitement des messages.
Tracer le traitement des messages
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");
}
}
}
Dans cet exemple, la télémétrie des demandes est signalée pour chaque message traité, avec un horodateur, une durée et un résultat (réussite). Les données de télémétrie comprennent également un jeu de propriétés de corrélation. Les traces et exceptions imbriquées qui sont signalées durant le traitement des messages sont également horodatées avec des propriétés de corrélation qui les représentent comme des « enfants » de RequestTelemetry
.
Si vous appelez des composants externes pris en charge durant le traitement des messages, ils sont également suivis et mis en corrélation automatiquement. Pour effectuer manuellement le suivi et la mise en corrélation, consultez Suivi des opérations personnalisées avec le kit SDK .NET d’Application Insights.
Si vous exécutez un code externe en plus du SDK Application Insights, attendez-vous à constater une durée plus longue lors de l’affichage des journaux Application Insights.
Cela ne signifie pas qu’il y a eu un retard lors de la réception du message. Dans ce scénario, le message a déjà été reçu, car il est passé en tant que paramètre au code du SDK. De plus, la balise name dans les journaux App Insights (Process) indique que le message est en cours de traitement par votre code de traitement d’événement externe. Ce problème n’est pas lié à Azure. Au lieu de cela, ces métriques font référence à l’efficacité de votre code externe, étant donné que le message a déjà été reçu de Service Bus.
Suivi avec OpenTelemetry
La version 7.5.0 de la bibliothèque de client Service Bus .NET et les versions ultérieures prennent en charge OpenTelemetry en mode expérimental. Pour plus d’informations, consultez Suivi distribué dans le SDK .NET.
Suivi sans un système de traçage
Si votre système de traçage ne prend pas en charge le suivi automatique des appels Service Bus, songez à l’ajouter dans un système de traçage ou dans votre application. Cette section décrit les événements de diagnostic envoyés par Service Bus .NET Client.
Service Bus .NET Client est instrumenté à l’aide des primitives de traçage .NET System.Diagnostics.Activity et System.Diagnostics.DiagnosticSource.
Activity
sert de contexte de trace, tandis que DiagnosticSource
est un mécanisme de notification.
S’il n’y a aucun écouteur pour les événements DiagnosticSource, l’instrumentation est désactivée et n’occasionne pas de frais. DiagnosticSource donne tout le contrôle à l’écouteur :
- L’écouteur contrôle les sources et les événements qu’il écoute
- L’écouteur contrôle l’échantillonnage et la fréquence des événements
- Les événements sont envoyés avec une charge utile qui fournit un contexte complet, ce qui vous permet d’accéder à l’objet Message durant l’événement et de le modifier
Avant de passer à l’implémentation, passez en revue le guide de l’utilisateur de DiagnosticSource.
Nous allons créer un écouteur pour les événements Service Bus dans une application ASP.NET Core qui écrit des journaux d’activité avec Microsoft.Extension.Logger. L’abonnement à DiagnosticSource s’effectue à l’aide de la bibliothèque System.Reactive.Core (vous pouvez aussi vous abonner facilement à DiagnosticSource sans cette bibliothèque).
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();
});
}
Dans cet exemple, l’écouteur journalise la durée, le résultat, l’identificateur unique et l’heure de début de chaque opération Service Bus.
Événements
Tous les événements ont les propriétés suivantes conformes à la spécification de télémétrie ouverte : https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md.
message_bus.destination
: chemin d’accès de file d’attente/rubrique/abonnementpeer.address
: espace de noms completkind
: producteur, consommateur ou client. Le producteur est utilisé lors de l’envoi de messages, consommateur lors de la réception et client lors du règlement.component
–servicebus
Tous les événements ont également des propriétés Entity
et Endpoint
.
Entity
: nom de l’entité (file d’attente, rubrique, etc.)Endpoint
: URL du point de terminaison Service Bus
Opérations instrumentées
Voici la liste complète des opérations instrumentées :
Nom d’opération | API suivie |
---|---|
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 | Rappel de processeur défini sur ServiceBusProcessor. Propriété ProcessMessageAsync |
ServiceBusSessionProcessor.ProcessSessionMessage | Rappel de processeur défini sur ServiceBusSessionProcessor. Propriété ProcessMessageAsync |
Filtrage et échantillonnage
Dans certains cas, il est souhaitable de journaliser uniquement une partie des événements pour réduire la surcharge des performances ou la consommation du stockage. Vous pouvez journaliser uniquement les événements « Stop » (comme dans l’exemple précédent) ou échantillonner un pourcentage des événements.
DiagnosticSource
offre un moyen d’y parvenir avec le prédicat IsEnabled
. Pour plus d’informations, consultez Filtrage basé sur le contexte dans DiagnosticSource.
IsEnabled
peut être appelé plusieurs fois pour une opération unique afin de minimiser l’impact sur les performances.
IsEnabled
est appelé dans la séquence suivante :
IsEnabled(<OperationName>, string entity, null)
, par exempleIsEnabled("ServiceBusSender.Send", "MyQueue1")
. Notez l’absence de « Start » ou de « Stop » à la fin. Utilisez-le pour exclure des opérations ou des files d’attente particulières. Si la méthode de rappel retournefalse
, les événements de l’opération ne sont pas envoyés.- Pour les opérations « Process » et « ProcessSession », vous recevez également le rappel
IsEnabled(<OperationName>, string entity, Activity activity)
. Utilisez-le pour filtrer des événements en fonction des propriétés des balises ou deactivity.Id
.
- Pour les opérations « Process » et « ProcessSession », vous recevez également le rappel
IsEnabled(<OperationName>.Start)
, par exempleIsEnabled("ServiceBusSender.Send.Start")
. Vérifie si l’événement « Start » doit être déclenché. Le résultat affecte uniquement l’événement « Start », mais l’instrumentation supplémentaire n’en dépend pas.
Il n’y a pas de IsEnabled
pour l’événement « Stop ».
Si le résultat d’une opération est une exception, IsEnabled("ServiceBusSender.Send.Exception")
est appelé. Vous pouvez uniquement vous abonner aux événements « Exception » et empêcher le reste de l’instrumentation. Dans ce cas, vous devez encore gérer ces exceptions. Toute autre instrumentation étant désactivée, ne vous attendez pas à ce que le contexte de trace circule avec les messages du consommateur au producteur.
Vous pouvez également utiliser IsEnabled
pour implémenter des stratégies d’échantillonnage. L’échantillonnage basé sur Activity.Id
ou Activity.RootId
garantit des résultats cohérents (à condition qu’il soit propagé par le système de suivi ou par votre propre code).
En présence de plusieurs écouteurs DiagnosticSource
pour la même source, il suffit qu’un seul écouteur accepte l’événement. L’appel de IsEnabled
n’est donc pas garanti.
Étapes suivantes
- Corrélation dans Application Insights
- Monitoring des dépendances Application Insights pour déterminer si REST, SQL ou d’autres ressources externes ralentissent vos opérations.
- Suivi des opérations personnalisées avec le kit SDK .NET d’Application Insights