Verteilte Ablaufverfolgung und Korrelation über Service Bus-Messaging
Eine der häufigsten Herausforderungen bei der Microservice-Entwicklung besteht darin, Abläufe von einem Client aus über alle an der Verarbeitung beteiligten Dienste zu verfolgen. Eine solche Fähigkeit ist nützlich für das Debuggen, die Leistungsanalyse, A/B-Tests und andere gängige Diagnoseszenarien. Ein Teil des Problems besteht darin, die logischen Arbeitsschritte nachzuverfolgen. Dies umfasst neben der Auswertung der Nachrichtenverarbeitung auch die Latenz und externe Abhängigkeitsaufrufe. Der andere Teil ist die Korrelation dieser Diagnoseereignisse über Prozessgrenzen hinweg.
Wenn ein Producer eine Nachricht über eine Warteschlange sendet, geschieht dies in der Regel im Rahmen eines anderen logischen Vorgangs, der von einem anderen Client oder Dienst initiiert wird. Derselbe Vorgang wird vom Consumer fortgesetzt, sobald dieser eine Nachricht empfängt. Meistens gibt sowohl der Producer als auch der Consumer (sowie andere an der Verarbeitung beteiligten Dienste) Telemetrieereignisse aus, die zur Nachverfolgung des Vorgangsflusses und des Ergebnisses herangezogen werden. Um diese Ereignisse zu korrelieren und den Vorgang bis zum Ende zu verfolgen, muss jeder Dienst, der Telemetriedaten meldet, die einzelnen Ereignisse mit einem Ablaufverfolgungskontext kennzeichnen. Eine Bibliothek, die Entwicklern dabei helfen kann, diese ganze Telemetrie standardmäßig auszugeben, ist NServiceBus.
Microsoft Azure Service Bus-Messaging verfügt über definierte Nutzlasteigenschaften, die sowohl von Producern als auch von Consumern zur Übergabe dieser Ablaufverfolgungskontexte verwendet werden müssen. Das Protokoll basiert auf dem W3C-Ablaufverfolgungskontext.
Eigenschaftenname | BESCHREIBUNG |
---|---|
Diagnostic-Id | Ein eindeutiger Bezeichner eines externen Aufrufs, der vom Producer an die Warteschlange gesendet wird. Informationen zum Format finden Sie unter traceparent-Header des Ablaufverfolgungskontexts. |
Automatische Ablaufverfolgung im Service Bus-Client für .NET
Die Klasse ServiceBusProcessor
des Azure Messaging-Service Bus-Clients für .NET stellt Instrumentierungspunkte für die Ablaufverfolgung bereit, die von Ablaufverfolgungssystemen oder von Clientcode verwendet werden können. Die Instrumentierung ermöglicht die Nachverfolgung aller Aufrufe, die von Clientseite an den Service Bus-Messagingdienst gesendet werden. Wenn die Nachrichtenverarbeitung über ProcessMessageAsync
von ServiceBusProcessor
(Meldungshandlermuster) erfolgt, wird die Nachrichtenverarbeitung ebenfalls instrumentiert.
Nachverfolgung mithilfe von Azure Application Insights
Microsoft Application Insights bietet umfassende Funktionen zur Leistungsüberwachung, darunter die automatische Nachverfolgung von Anforderungen und Abhängigkeiten.
Installieren Sie je nach Projekttyp ggf. das Application Insights SDK:
- ASP.NET: Installieren Sie Version 2.5-beta2 oder höher.
- ASP.NET Core: Installieren Sie Version 2.2.0-beta2 oder höher. Über diese Links können Sie Details zur Erstellung von Ressourcen sowie zur Installation und Konfiguration des SDKs (falls erforderlich) abrufen. Weitere Informationen zu Nicht-ASP.NET-Anwendungen finden Sie im Artikel Azure Application Insights für Konsolenanwendungen.
Wenn Sie ProcessMessageAsync
von ServiceBusProcessor
(Meldungshandlermuster) zur Verarbeitung von Nachrichten verwenden, wird die Nachrichtenverarbeitung ebenfalls instrumentiert. Alle von Ihrem Dienst durchgeführten Service Bus-Aufrufe werden automatisch nachverfolgt und mit anderen Telemetrieelementen korreliert. Andernfalls finden Sie im folgenden Beispiel Informationen zur manuellen Nachverfolgung der Nachrichtenverarbeitung.
Nachrichtenverarbeitung nachverfolgen
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 diesem Beispiel wird für jede verarbeitete Nachricht Anforderungstelemetrie einschließlich Zeitstempel, Dauer und Ergebnis (Erfolg) gemeldet. Zur Telemetrie gehört auch eine Gruppe von Korrelationseigenschaften. Geschachtelte Ablaufverfolgungen und Ausnahmen, die während der Nachrichtenverarbeitung gemeldet werden, werden ebenfalls mit Korrelationseigenschaften gekennzeichnet, um sie als "untergeordnete Elemente" von RequestTelemetry
auszuweisen.
Falls Sie während der Nachrichtenverarbeitung unterstützte externe Komponenten aufrufen, werden diese ebenfalls automatisch nachverfolgt und korreliert. Weitere Informationen zur manuellen Nachverfolgung und Korrelation finden Sie unter Nachverfolgen benutzerdefinierter Vorgänge mit dem Application Insights .NET SDK.
Wenn Sie zusätzlich zum Application Insights SDK externen Code ausführen, sollten Sie bei der Anzeige von Application Insights-Protokollen von einer längeren Dauer ausgehen.
Dies bedeutet nicht, dass eine Verzögerung beim Empfangen der Nachricht aufgetreten ist. In diesem Szenario wurde die Nachricht bereits empfangen, weil die Nachricht als Parameter an den SDK-Code übergeben wird. Außerdem gibt das Tag Name in App Insights-Protokollen (Process) an, dass die Nachricht nun von Ihrem externen Ereignisverarbeitungscode verarbeitet wird. Dieses Problem bezieht sich nicht auf Azure. Stattdessen verweisen diese Metriken auf die Effizienz Ihres externen Codes, wenn die Nachricht bereits von Service Bus empfangen wurde.
Nachverfolgung mit OpenTelemetry
Version 7.5.0 der .NET-Clientbibliothek für Service Bus unterstützt OpenTelemetry im experimentellen Modus. Weitere Informationen finden Sie unter Distributed tracing (Verteilte Ablaufverfolgung in .NET SDK).
Nachverfolgung ohne Ablaufverfolgungssystem
Falls die automatische Nachverfolgung von Service Bus-Aufrufen von Ihrem Ablaufverfolgungssystem nicht unterstützt wird, empfiehlt es sich, das Ablaufverfolgungssystem oder die Anwendung entsprechend zu erweitern. In diesem Abschnitt werden die Diagnoseereignisse beschrieben, die vom Service Bus .NET-Client gesendet werden.
Der Service Bus .NET-Client verwendet zwei primitive Typen, um die .NET-Ablaufverfolgung zu instrumentieren: System.Diagnostics.Activity und System.Diagnostics.DiagnosticSource.
Activity
dient als Ablaufverfolgungskontext, während DiagnosticSource
ein Benachrichtigungsmechanismus ist.
Falls kein Listener für die DiagnosticSource-Ereignisse vorhanden ist, wird die Instrumentierung deaktiviert. In diesem Fall liegen die Instrumentierungskosten bei null. DiagnosticSource übergibt die Kontrolle vollständig an den Listener:
- Der Listener steuert, welche Datenquellen und Ereignisse überwacht werden.
- Der Listener steuert die Ereignisrate und den Stichprobenumfang.
- Ereignisse werden mit einer Nutzlast gesendet, die vollständigen Kontext bereitstellt. Auf diese Weise können Sie das Message-Objekt während des Ereignisses abrufen und ändern.
Machen Sie sich mit dem DiagnosticSource-Benutzerleitfaden vertraut, bevor Sie mit der Implementierung fortfahren.
Als Nächstes erstellen wir einen Listener für Service Bus-Ereignisse in der ASP.NET Core-App, die die Protokollierung mithilfe von Microsoft.Extension.Logger durchführt. Dabei wird die System.Reactive.Core-Bibliothek verwendet, um DiagnosticSource zu abonnieren (aber auch ohne Bibliothek kann DiagnosticSource ganz einfach abonniert werden).
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 diesem Beispiel werden die Dauer, das Ergebnis, der eindeutige Bezeichner und die Startzeit der einzelnen Service Bus-Vorgänge vom Listener protokolliert.
Events
Alle Ereignisse verfügen über die folgenden Eigenschaften, die der Spezifikation für offene Telemetrie entsprechen: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md.
message_bus.destination
: Pfad zu Warteschlange/Thema/Abonnement.peer.address
: Vollqualifizierter Namespace.kind
: Producer, Consumer oder Client. Der Producer wird beim Senden, der Consumer beim Empfangen und der Client beim Speichern von Nachrichten verwendet.component
–servicebus
Alle Ereignisse haben auch die Eigenschaften Entity
und Endpoint
.
Entity
– der Name der Entität (z. B. Warteschlange oder Thema)Endpoint
– die URL des Service Bus-Endpunkts
Instrumentierte Vorgänge
Im Folgenden finden Sie eine vollständige Liste der instrumentierten Vorgänge:
Vorgangsname | Nachverfolgte 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 | Der für ServiceBusProcessor festgelegte Prozessorrückruf. Eigenschaft „ProcessMessageAsync“ |
ServiceBusSessionProcessor.ProcessSessionMessage | Der für ServiceBusSessionProcessor festgelegte Prozessorrückruf. Eigenschaft „ProcessMessageAsync“ |
Filtern und Stichprobenumfang
In einigen Fällen ist es wünschenswert, nur einen Teil der Ereignisse zu protokollieren, um den zusätzlichen Leistungsaufwand oder die Speicherauslastung zu reduzieren. Beispielsweise können Sie nur Stop-Ereignisse (wie im vorangehenden Beispiel) oder einen Stichprobenprozentsatz protokollieren.
DiagnosticSource
bietet zu diesem Zweck das IsEnabled
-Prädikat. Weitere Informationen finden Sie unter Kontextbasierte Filterung in DiagnosticSource.
IsEnabled
kann mehrmals für einen einzelnen Vorgang aufgerufen werden, um Leistungseinbußen zu minimieren.
IsEnabled
wird in der folgenden Abfolge aufgerufen:
IsEnabled(<OperationName>, string entity, null)
entsprichtIsEnabled("ServiceBusSender.Send", "MyQueue1")
. Hinweis: Das Start- und das Stop-Ereignis am Ende fehlen. Verwenden Sie das Element, um bestimmte Vorgänge oder Warteschlangen herauszufiltern. Wenn durch die Rückrufmethodefalse
zurückgegeben wird, werden keine Ereignisse für den Vorgang gesendet.- Für den Process-Vorgang und den ProcessSession-Vorgang wird ebenfalls ein
IsEnabled(<OperationName>, string entity, Activity activity)
-Rückruf empfangen. Verwenden Sie das Element, um Ereignisse auf der Basis vonactivity.Id
-Eigenschaften oder Tags-Eigenschaften zu filtern.
- Für den Process-Vorgang und den ProcessSession-Vorgang wird ebenfalls ein
IsEnabled(<OperationName>.Start)
entsprichtIsEnabled("ServiceBusSender.Send.Start")
. Durch diesen Code wird überprüft, ob das Start-Ereignis ausgelöst werden soll. Das Ergebnis wirkt sich nur auf das Start-Ereignis, nicht aber auf die weitere Instrumentierung aus.
Für das Stop-Ereignis ist keine IsEnabled
-Eigenschaft vorhanden.
Wenn ein Vorgang eine Ausnahme zurückgibt, wird IsEnabled("ServiceBusSender.Send.Exception")
aufgerufen. Sie können auch nur Exception-Ereignisse abonnieren und die übrigen Instrumentierungsschritte auslassen. In diesem Fall müssen solche Ausnahmen trotzdem behandelt werden. Da die weitere Instrumentierung deaktiviert ist, werden die Nachrichten zwischen Consumer und Producer ohne Ablaufverfolgungskontext weitergeleitet.
Sie können IsEnabled
auch zur Implementierung von Stichprobenstrategien verwenden. Durch die Stichprobenentnahme auf der Grundlage von Activity.Id
oder Activity.RootId
wird über alle Ebenen eine konsistente Stichprobenentnahme sichergestellt (sofern die Weiterleitung über das Ablaufverfolgungssystem oder den eigenen Code erfolgt).
Falls mehrere DiagnosticSource
-Listener für dieselbe Quelle vorhanden sind, muss nur ein Listener das Ereignis akzeptieren. Es kann also sein, dass IsEnabled
nicht aufgerufen wird.
Nächste Schritte
- Application Insights-Korrelation
- Abhängigkeitsüberwachung in Application Insights, um zu überprüfen, ob REST, SQL oder andere externe Ressourcen Ihr System verlangsamen.
- Nachverfolgen von benutzerdefinierten Vorgängen mit dem Application Insights .NET SDK