Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Artikel werden Fehleruntersuchungstechniken, Parallelität und häufige Fehler in der Azure Service Bus Java-Clientbibliothek behandelt. Verwenden Sie diese Anleitung, um Die Ursachen zu identifizieren und Gegenmaßnahmen anzuwenden, um Azure Service Bus Probleme schneller zu beheben.
Aktivieren und Konfigurieren der Protokollierung
Das Azure SDK für Java bietet ein einheitliches Protokollierungskonzept, das bei der Fehlerbehebung von Anwendungen unterstützt und die Lösung von Problemen beschleunigt. Die Protokolle, die Sie erstellen, erfassen den Fluss einer Anwendung, bevor sie den Terminalstatus erreicht, um das Stammproblem zu finden. Richtlinien zur Protokollierung finden Sie unter Konfiguration der Protokollierung im Azure SDK für Java und Übersicht zur Fehlerbehebung.
Zusätzlich zur Aktivierung der Protokollierung bietet das Festlegen der Protokollebene auf VERBOSE oder DEBUG Einblicke in den Zustand der Bibliothek. In den folgenden Abschnitten finden Sie Beispiele für die Konfiguration von log4j2 und logback, um die übermäßigen Meldungen zu reduzieren, wenn die ausführliche Protokollierung aktiviert ist.
Konfigurieren von Log4J 2
Führen Sie die folgenden Schritte aus, um Log4J 2 zu konfigurieren:
- Fügen Sie die Abhängigkeiten in Ihrer pom.xml hinzu, indem Sie diese aus dem Protokollierungsbeispiel pom.xmlverwenden, im Abschnitt "Für Log4j2 erforderliche Abhängigkeiten".
- Fügen Sie log4j2.xml zu Ihrem src/main/resources-Ordner hinzu.
Logback konfigurieren
Führen Sie die folgenden Schritte aus, um Logback zu konfigurieren:
- Fügen Sie die Abhängigkeiten in Ihrem pom.xml hinzu, indem Sie diese aus dem Protokollierungsbeispiel pom.xmlverwenden, im Abschnitt "Für logback erforderliche Abhängigkeiten".
- Fügen Sie logback.xml zu Ihrem src/main/resources-Ordner hinzu.
Aktivierung der AMQP-Transportprotokollierung
Wenn die Aktivierung der Client-Protokollierung nicht ausreicht, um Ihre Probleme zu diagnostizieren, können Sie die Protokollierung in eine Datei in der zugrunde liegenden AMQP-Bibliothek, Qpid Proton-J, aktivieren. Qpid Proton-J verwendet java.util.logging. Sie können die Protokollierung aktivieren, indem Sie eine Konfigurationsdatei mit den im nächsten Abschnitt gezeigten Inhalten erstellen. Oder Sie setzen proton.trace.level=ALL und die gewünschten Konfigurationsoptionen für die java.util.logging.Handler-Implementierung. Die Implementierungsklassen und deren Optionen finden Sie in der Java 8 SDK-Dokumentation unter Package java.util.logging.
Um die Ablaufverfolgung der AMQP-Transportrahmen durchzuführen, setzen Sie die Umgebungsvariable PN_TRACE_FRM=1.
Beispiel für die Datei logging.properties
Die folgende Konfigurationsdatei protokolliert die Ablaufverfolgung auf TRACE-Ebene von Proton-J in der Datei proton-trace.log:
handlers=java.util.logging.FileHandler
.level=OFF
proton.trace.level=ALL
java.util.logging.FileHandler.level=ALL
java.util.logging.FileHandler.pattern=proton-trace.log
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=[%1$tF %1$tr] %3$s %4$s: %5$s %n
Protokollierung reduzieren
Eine Möglichkeit, die Protokollierung zu verringern, besteht darin, die Ausführlichkeit zu ändern. Eine andere Möglichkeit ist das Hinzufügen von Filtern, die Protokolle von Paketen mit Loggernamen wie com.azure.messaging.servicebus oder com.azure.core.amqp ausschließen. Beispiele finden Sie in den XML-Dateien in den Abschnitten Konfiguration von Log4J 2 und Konfiguration von logback.
Wenn Sie einen Fehler übermitteln, sind die Protokollmeldungen aus Klassen in den folgenden Paketen interessant:
com.azure.core.amqp.implementationcom.azure.core.amqp.implementation.handler- Die Ausnahme ist, dass Sie die Nachricht
onDeliveryinReceiveLinkHandlerignorieren können.
- Die Ausnahme ist, dass Sie die Nachricht
com.azure.messaging.servicebus.implementation
Gleichzeitigkeit im ServiceBusProcessorClient
ServiceBusProcessorClient ermöglicht es Ihnen, zu konfigurieren, wie viele Aufrufe des Nachrichtenhandlers gleichzeitig ausgeführt werden. Diese Konfiguration ermöglicht es, mehrere Nachrichten parallel zu verarbeiten. Für ein ServiceBusProcessorClient, das Nachrichten aus einer Entität ohne Sitzungen verarbeitet, können Sie die gewünschte Parallelität über die maxConcurrentCalls-API festlegen. Für eine sitzungsaktivierte Entität beträgt die gewünschte Parallelität das maxConcurrentSessions-Fache von maxConcurrentCalls.
Wenn Sie weniger gleichzeitige Aufrufe des Nachrichtenhandlers als die konfigurierte Parallelität beobachten, liegt dies möglicherweise daran, dass der Threadpool nicht entsprechend angepasst wird.
ServiceBusProcessorClient verwendet Daemon-Threads aus dem globalen BoundedElastic-Thread-Pool von Reactor, um den Message-Handler aufzurufen. Die maximale Anzahl gleichzeitiger Threads in diesem Pool ist durch eine Obergrenze begrenzt. Diese Obergrenze beträgt standardmäßig zehn mal die Anzahl der verfügbaren CPU-Kerne. Damit ServiceBusProcessorClient die von der Anwendung gewünschte Gleichzeitigkeit (maxConcurrentCalls- oder maxConcurrentSessions-mal maxConcurrentCalls) effektiv unterstützen kann, müssen Sie einen boundedElastic pool cap-Wert haben, der höher ist als die gewünschte Gleichzeitigkeit. Sie können die Standardbegrenzung überschreiben, indem Sie die Systemeigenschaft reactor.schedulers.defaultBoundedElasticSizefestlegen.
Optimieren Sie den Threadpool und die CPU-Zuweisung auf Fall-für-Fall-Basis. Wenn Sie jedoch die Poolbegrenzung außer Kraft setzen, beschränken Sie als Ausgangspunkt die gleichzeitigen Threads auf ca. 20-30 pro CPU-Kern. Begrenzen Sie die gewünschte Parallelität pro ServiceBusProcessorClient-Instanz auf etwa 20 bis 30. Analysieren und messen Sie Ihren spezifischen Anwendungsfall, und optimieren Sie die Aspekte der Gleichzeitigkeit entsprechend. Bei Szenarien mit hoher Auslastung sollten Sie mehrere ServiceBusProcessorClient Instanzen ausführen, in denen jede Instanz aus einer neuen ServiceBusClientBuilder Instanz erstellt wird. Erwägen Sie außerdem, jeden ServiceBusProcessorClient in einem dedizierten Host auszuführen, beispielsweise in einem Container oder einer VM, damit die Ausfallzeiten eines Hosts die gesamte Nachrichtenverarbeitung nicht beeinträchtigen.
Beachten Sie, dass das Festlegen eines hohen Werts für die Poolgrenze auf einem Host mit wenigen CPU-Kernen nachteilige Auswirkungen hat. Einige Anzeichen für geringe CPU-Ressourcen oder einen Pool mit zu vielen Threads auf weniger CPUs sind: häufige Timeouts, verlorene Sperren, Deadlocks oder geringerer Durchsatz. Wenn Sie die Java Anwendung auf einem Container ausführen, verwenden Sie zwei oder mehr vCPU-Kerne. Wählen Sie beim Ausführen einer Java-Anwendung in containerisierten Umgebungen nicht weniger als 1 vCPU-Kern aus. Ausführliche Empfehlungen zur Ressourcenausstattung finden Sie unter Containerisierung Ihrer Java-Anwendungen.
Engpass bei der gemeinsamen Nutzung von Verbindungen
Alle Clients, die Sie aus einer freigegebenen ServiceBusClientBuilder Instanz erstellen, verwenden dieselbe Verbindung mit dem Service Bus Namespace.
Die Verwendung einer gemeinsam genutzten Verbindung ermöglicht das Multiplexen von Operationen zwischen Clients auf einer Verbindung, aber die gemeinsame Nutzung kann auch zu einem Engpass werden, wenn es viele Clients gibt oder die Clients zusammen eine hohe Last erzeugen. Jeder Verbindung ist ein E/A-Thread zugeordnet. Wenn Sie eine Verbindung gemeinsam nutzen, stellen die Clients ihre Arbeit in die Arbeitswarteschlange dieses gemeinsamen E/A-Threads, und der Fortschritt jedes Clients hängt davon ab, dass seine Arbeit in der Warteschlange rechtzeitig abgeschlossen wird. Der E/A-Thread bearbeitet die in der Warteschlange stehende Arbeit seriell. Das heißt, wenn sich in der Arbeitswarteschlange des E/A-Threads einer gemeinsam genutzten Verbindung schließlich viele ausstehende Aufgaben ansammeln, dann sind die Symptome ähnlich wie bei geringer CPU-Leistung. Diese Bedingung wird im vorherigen Abschnitt über Gleichzeitigkeit beschrieben – z. B. Clients, die blockieren, Timeout, verlorene Sperre oder Verlangsamung des Wiederherstellungspfads.
Service Bus SDK verwendet das reactor-executor-*-Namensmuster für den Verbindungs-E/A-Thread. Wenn bei der Anwendung ein Engpass bei der gemeinsam genutzten Verbindung auftritt, kann sich dies in der CPU-Auslastung des E/A-Threads zeigen. Auch im Heap-Dump oder im Live-Speicher ist das Objekt ReactorDispatcher$workQueue die Arbeitswarteschlange des E/A-Threads. Eine lange Arbeitswarteschlange im Speicherabbild während des Engpasses kann darauf hindeuten, dass der gemeinsam genutzte E/A-Thread mit ausstehenden Aufgaben überlastet ist.
Wenn die Auslastung der Anwendung für einen Service Bus-Endpunkt also relativ hoch ist, gemessen an der Gesamtzahl der gesendeten und empfangenen Nachrichten oder an der Größe der Nutzlast, verwenden Sie für jeden Client, den Sie erstellen, eine separate Builder-Instanz. Zum Beispiel können Sie für jede Entität – Queue oder Thema – ein neues ServiceBusClientBuilder erstellen und daraus einen Client bauen. Bei extrem hoher Auslastung einer bestimmten Entität können Sie entweder mehrere Clientinstanzen für diese Entität erstellen oder Clients auf mehreren Hosts ausführen, z. B. in Containern oder VMs, um den Lastenausgleich durchzuführen.
Clients bleiben bei Verwendung des benutzerdefinierten Endpunkts von Application Gateway stehen
Die benutzerdefinierte Endpunktadresse bezieht sich auf eine von der Anwendung bereitgestellte HTTPS-Endpunktadresse, die Sie in Service Bus auflösen oder konfigurieren können, um Datenverkehr an Service Bus weiterzuleiten. Azure Application Gateway erleichtert das Erstellen eines HTTPS-Front-Ends, das Datenverkehr an Service Bus weiterleitet. Sie können das Service Bus SDK für eine Anwendung so konfigurieren, dass eine Front-End-IP-Adresse des Anwendungsgateways als benutzerdefinierter Endpunkt verwendet wird, um eine Verbindung mit Service Bus herzustellen.
Das Anwendungsgateway bietet mehrere Sicherheitsrichtlinien, die unterschiedliche TLS-Protokollversionen unterstützen. Es gibt vordefinierte Richtlinien, die TLS 1.2 als Mindestversion erzwingen, und es gibt ältere Richtlinien, die TLS 1.0 als Mindestversion verwenden. Sie wenden eine TLS-Richtlinie auf das HTTPS-Front-End an.
Derzeit erkennt das Service Bus SDK bestimmte Remote-TCP-Beendigungen nicht vom Front-End des Anwendungsgateways, das TLS 1.0 als Mindestversion verwendet. Wenn das Front-End beispielsweise TCP FIN- und ACK-Pakete sendet, um die Verbindung zu schließen, wenn sie seine Eigenschaften aktualisiert, kann das SDK diese Pakete nicht erkennen. Die Verbindung wird also nicht wiederhergestellt, und Clients können nachrichten nicht mehr senden oder empfangen. Dieser Halt geschieht nur, wenn TLS 1.0 als Mindestversion verwendet wird. Um dieses Problem zu beheben, verwenden Sie eine Sicherheitsrichtlinie, die TLS 1.2 oder höher als Mindestversion für das Front-End des Anwendungsgateways festlegt.
Die Unterstützung für TLS 1.0 und 1.1 in allen Azure-Diensten wird bis zum 31. Oktober 2024 eingestellt, wie bereits angekündigt; stellen Sie daher auf TLS 1.2 um.
Nachricht oder Sitzungssperre ist verloren gegangen
Ein Servicebus-Queue oder ein Themenabonnement hat eine auf Ressourcenebene festgelegte Sperrdauer. Wenn der Empfänger-Client eine Nachricht aus der Ressource abruft, wendet der Service-Bus-Broker eine erste Sperre auf die Nachricht an. Die anfängliche Sperre hält für die auf der Ressourcenebene eingestellte Sperrdauer an. Wenn die Nachrichtensperre nicht verlängert wird, bevor sie abläuft, gibt der Service Bus Broker die Nachricht frei, um sie für andere Empfänger verfügbar zu machen. Wenn die Anwendung versucht, eine Nachricht nach ablauf der Sperre abzuschließen oder aufzugeben, schlägt der API-Aufruf mit dem Fehler com.azure.messaging.servicebus.ServiceBusException: The lock supplied is invalid. Either the lock expired, or the message has already been removed from the queuefehl.
Der Service Bus-Client unterstützt die Ausführung einer Aufgabe zur Erneuerung der Sperre im Hintergrund, die die Nachrichtensperre jedes Mal erneuert, bevor sie abläuft. Standardmäßig wird die Sperrenverlängerungsaufgabe 5 Minuten lang ausgeführt. Sie können die Dauer der Sperrverlängerung mit ServiceBusReceiverClientBuilder.maxAutoLockRenewDuration(Duration) anpassen. Wenn Sie den Wert Duration.ZERO angeben, wird die Aufgabe Sperren erneuern deaktiviert.
Die folgende Liste beschreibt einige der Nutzungsmuster oder Hostumgebungen, die zum Fehler „Sperre verloren“ führen können:
Der Vorgang zum Verlängern der Sperre ist deaktiviert, und die Nachrichtenverarbeitungszeit der Anwendung überschreitet die auf Ressourcenebene festgelegte Sperrdauer.
Die Nachrichtenverarbeitungszeit der Anwendung überschreitet die konfigurierte Dauer der Sperre der Aufgabe. Beachten Sie, dass, wenn die Verlängerungsdauer der Sperre nicht explizit festgelegt ist, standardmäßig auf 5 Minuten festgelegt wird.
Die Anwendung aktiviert die Prefetch-Funktion, indem sie den Prefetch-Wert mithilfe von
ServiceBusReceiverClientBuilder.prefetchCount(prefetch)auf eine positive ganze Zahl festlegt. Wenn die Prefetchfunktion aktiviert ist, ruft der Client aus der Service Bus-Entität – Warteschlange oder Topic – die Anzahl von Nachrichten ab, die der Prefetch-Anzahl entspricht, und speichert sie im Prefetchpuffer im Arbeitsspeicher. Die Nachrichten bleiben im Prefetch-Puffer, bis sie in der Anwendung empfangen werden. Der Client verlängert die Sperre der Nachrichten nicht, solange sie sich im Prefetch-Puffer befinden. Wenn die Anwendungsverarbeitung so lange dauert, dass Nachrichtensperren ablaufen, während sie im Vorabstartpuffer bleiben, kann die Anwendung die Nachrichten mit einer abgelaufenen Sperre abrufen. Weitere Informationen finden Sie unter Warum ist Prefetch nicht die Standardoption?In der Hostumgebung treten gelegentlich Netzwerkprobleme auf – z. B. ein vorübergehender Netzwerkausfall –, die verhindern, dass die Aufgabe zur Sperrenerneuerung die Sperre rechtzeitig erneuert.
In der Hostumgebung fehlen genügend CPUs oder es mangelt zeitweise an CPU-Zyklen, was den Vorgang zur Erneuerung der Sperre daran hindert, rechtzeitig ausgeführt zu werden.
Die Zeit des Hostsystems ist nicht genau – die Uhr geht zum beispielsweise nicht richtig. Dadurch verzögert sich die Sperraufgabe und kann nicht pünktlich ausgeführt werden.
Der Verbindungs-E/A-Thread ist überlastet, was sich auf seine Fähigkeit auswirkt, Anfragen zur Sperrenerneuerung im Netzwerk rechtzeitig auszuführen. Die folgenden beiden Szenarien können dieses Problem verursachen:
- In der Anwendung laufen zu viele Empfängerclients, die sich dieselbe Verbindung teilen. Weitere Informationen finden Sie im Abschnitt Engpass bei der gemeinsamen Nutzung von Verbindungen.
- Die Anwendung konfiguriert
ServiceBusReceiverClient.receiveMessagesoderServiceBusProcessorClientso, dass sie großemaxMessages- odermaxConcurrentCalls-Werte aufweisen. Weitere Informationen finden Sie im Abschnitt Gleichzeitigkeit im ServiceBusProcessorClient.
Ein gängiges Anwendungsmuster, das die Wahrscheinlichkeit eines verloren gegangenen Sperrfehlers erhöht, umfasst die Planung lang ausgeführter Sperrverlängerungsaufgaben – z. B. Vorgänge mit Dauer, die mehrere Stunden umfassen. Wie bereits erwähnt, können verschiedene Faktoren außerhalb der Kontrolle eines Service Bus-Clients eine erfolgreiche Sperrverlängerung beeinträchtigen, sodass Anwendungsdesigns eine garantierte Verlängerung über längere Zeiträume vermeiden sollten. Um zu vermeiden, dass lange ausgeführte Vorgänge erneut verarbeitet werden müssen, sollten Sie die Arbeit in kleinere Blöcke aufteilen oder idempotente Prüfpunktlogik implementieren.
Die Anzahl der Sperrenerneuerungsaufgaben im Client entspricht den maxMessages- oder maxConcurrentCalls-Parameterwerten, die für ServiceBusProcessorClient oder ServiceBusReceiverClient.receiveMessages festgelegt wurden. Eine hohe Anzahl von Tasks zur Sperrenerneuerung, die mehrere Anfragen an das Netzwerk stellen, kann sich auch negativ auf die Drosselung des Servicebus-Namespace auswirken.
Wenn der Host nicht ausreichend ressourcenschonend ist, kann die Sperre auch dann verloren gehen, wenn nur einige Sperrverlängerungsaufgaben ausgeführt werden. Wenn Sie die Java Anwendung auf einem Container ausführen, verwenden Sie zwei oder mehr vCPU-Kerne. Wählen Sie bei der Ausführung von Java Anwendungen in containerisierten Umgebungen nichts anderes als 1 vCPU-Kern aus. Ausführliche Empfehlungen zur Ressourcenausstattung finden Sie unter Containerisierung Ihrer Java-Anwendungen.
Die gleichen Anmerkungen zu Sperren gelten auch für eine Service-Bus-Warteschlange oder ein Themenabonnement, für das eine Sitzung aktiviert ist. Wenn der Empfänger-Client eine Verbindung zu einer Sitzung in der Ressource herstellt, wendet der Broker eine erste Sperre auf die Sitzung an. Um die Sitzungssperre beizubehalten, muss der Prozess zum Erneuern der Sperre im Client die Sitzungssperre ständig erneuern, bevor sie abläuft. Bei einer sitzungsfähigen Ressource werden die zugrundeliegenden Partitionen manchmal verschoben, um einen Lastausgleich zwischen den Servicebus-Knoten zu erreichen – beispielsweise, wenn neue Knoten hinzugefügt werden, um die Last zu verteilen. Wenn das passiert, können Sitzungssperren verloren gehen. Wenn die Anwendung versucht, eine Nachricht nach dem Verlust der Sitzungssperre abzuschließen oder aufzugeben, schlägt der API-Aufruf mit dem Fehler com.azure.messaging.servicebus.ServiceBusException: The session lock was lost. Request a new session receiverfehl.
Nächste Schritte
Wenn die Anleitung zur Problembehandlung in diesem Artikel beim Verwenden der Azure SDK für Java Clientbibliotheken nicht hilft, geben Sie ein Problem im Azure SDK für Java GitHub Repository an.