Delen via


Problemen met Azure Service Bus oplossen

In dit artikel worden technieken voor foutonderzoek, gelijktijdigheid, veelvoorkomende fouten voor de referentietypen in de Azure Service Bus Java-clientbibliotheek beschreven en worden oplossingen voor deze fouten beschreven.

Logboekregistratie inschakelen en configureren

Azure SDK voor Java biedt een consistente logregistratie om te helpen bij het oplossen van toepassingsfouten en hun oplossing te versnellen. De logboeken die worden geproduceerd, leggen de stroom van een toepassing vast voordat de terminalstatus wordt bereikt om het hoofdprobleem op te sporen. Zie Logboekregistratie configureren in de Azure SDK voor Java en Overzicht van probleemoplossingvoor hulp bij logboekregistratie.

Naast het inschakelen van logboekregistratie biedt het instellen van het logboekniveau op VERBOSE of DEBUG inzicht in de status van de bibliotheek. In de volgende secties ziet u voorbeeldconfiguraties voor log4j2 en logback om de overmatige berichten te verminderen wanneer uitgebreide logboekregistratie is ingeschakeld.

Log4J 2 configureren

Gebruik de volgende stappen om Log4J 2 te configureren:

  1. Voeg de afhankelijkheden in uw pom.xml toe met behulp van afhankelijkheden uit het voorbeeld van logboekregistratie pom.xmlin de sectie 'Afhankelijkheden vereist voor Log4j2'.
  2. Voeg log4j2.xml toe aan je src/main/resources directory.

Logback configureren

Gebruik de volgende stappen om logback te configureren:

  1. Voeg de afhankelijkheden in uw pom.xml toe met behulp van afhankelijkheden uit het voorbeeld van logboekregistratie pom.xmlin de sectie 'Afhankelijkheden vereist voor logback'.
  2. Voeg logback.xml toe aan uw src/main/resources map.

AMQP-transportlogboekregistratie inschakelen

Als het inschakelen van clientlogboekregistratie niet voldoende is om uw problemen vast te stellen, kunt u logboekregistratie inschakelen in een bestand van de onderliggende AMQP-bibliotheek, Qpid Proton-J. Qpid-Proton-J maakt gebruik van java.util.logging. U kunt logboekregistratie inschakelen door een configuratiebestand te maken met de inhoud die wordt weergegeven in de volgende sectie. Of stel proton.trace.level=ALL en de gewenste configuratieopties in voor de java.util.logging.Handler-implementatie. Zie Package java.util.logging in de Java 8 SDK-documentatie voor de implementatieklassen en de bijbehorende opties.

Als u de AMQP-transportframes wilt traceren, stelt u de omgevingsvariabele PN_TRACE_FRM=1 in.

Voorbeeldbestand logging.properties

Het volgende configuratiebestand logt TRACE-niveau uitvoer van Proton-J naar het bestand 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

Logging verminderen

Een manier om logboekregistratie te verminderen, is door de uitgebreidheid te wijzigen. Een andere manier is om filters toe te voegen die logboeken uitsluiten van loggernaampakketten zoals com.azure.messaging.servicebus of com.azure.core.amqp. Zie de XML-bestanden in de secties Configure Log4J 2 en Configure Logback.

Wanneer u een fout verzendt, zijn de logboekberichten van klassen in de volgende pakketten interessant:

  • com.azure.core.amqp.implementation
  • com.azure.core.amqp.implementation.handler
    • De uitzondering is dat u het onDelivery bericht in ReceiveLinkHandlerkunt negeren.
  • com.azure.messaging.servicebus.implementation

Gelijktijdigheid in ServiceBusProcessorClient

ServiceBusProcessorClient stelt de toepassing in staat om te configureren hoeveel aanroepen naar de berichtenhandler gelijktijdig moeten plaatsvinden. Met deze configuratie kunt u meerdere berichten parallel verwerken. Voor een ServiceBusProcessorClient berichten van een entiteit zonder sessie gebruiken, kan de toepassing de gewenste gelijktijdigheid configureren met behulp van de maxConcurrentCalls-API. Voor een entiteit met een ingeschakelde sessie is de gewenste gelijktijdigheid maxConcurrentSessions keer maxConcurrentCalls.

Als de toepassing minder gelijktijdige aanroepen naar de berichthandler ziet dan de geconfigureerde gelijktijdigheid, kan dit komen doordat de threadgroep niet de juiste grootte heeft.

ServiceBusProcessorClient gebruikt daemon-threads uit de globale Reactor-boundedElastic- threadpool om de berichthandler aan te roepen. Het maximum aantal gelijktijdige threads in deze groep wordt beperkt door een limiet. Deze limiet is standaard tien keer het aantal beschikbare CPU-kernen. Opdat ServiceBusProcessorClient de gewenste gelijktijdigheid (maxConcurrentCalls of maxConcurrentSessions keer maxConcurrentCalls) van de toepassing effectief ondersteunt, moet u een limietwaarde voor boundedElastic hebben die hoger is dan de gewenste gelijktijdigheid. U kunt de standaardlimiet overschrijven door de systeemeigenschap in te stellen reactor.schedulers.defaultBoundedElasticSize.

U moet de threadpool en CPU-toewijzing afstemmen per geval. Wanneer u echter de poollimiet overschrijdt, beperk dan als uitgangspunt de gelijktijdige threads tot ongeveer 20-30 per CPU-kern. Wij raden aan de gewenste gelijktijdigheid per ServiceBusProcessorClient instantie te beperken tot ongeveer 20-30. Profileer en meet uw specifieke use case en stem de gelijktijdigheidsaspecten dienovereenkomstig af. Voor scenario's met hoge belasting kunt u meerdere ServiceBusProcessorClient instanties uitvoeren waarbij elk exemplaar is gebouwd op basis van een nieuwe ServiceBusClientBuilder-instantie. Overweeg ook om elke ServiceBusProcessorClient uit te voeren in een toegewezen host, zoals een container of VM, zodat downtime in één host geen invloed heeft op de algehele berichtverwerking.

Houd er rekening mee dat het instellen van een hoge waarde voor de poollimiet op een host met weinig CPU-kernen nadelige gevolgen zou hebben. Sommige tekenen van lage CPU-resources of een pool met te veel threads op minder CPU's zijn: frequente time-outs, vergrendeling verloren, impasse of lagere doorvoer. Als u de Java-toepassing uitvoert in een container, raden we u aan twee of meer vCPU-kernen te gebruiken. Het wordt afgeraden om iets minder dan 1 vCPU-kern te selecteren bij het uitvoeren van een Java-toepassing in een containeromgeving. Zie Containerize uw Java-toepassingenvoor diepgaande aanbevelingen over resourcebeheer.

Knelpunt voor delen van verbinding

Alle clients die zijn gemaakt op basis van een gedeeld ServiceBusClientBuilder-exemplaar, delen dezelfde verbinding met de Service Bus-naamruimte.

Door een gedeelde verbinding te gebruiken, kunnen multiplexingbewerkingen tussen clients op één verbinding worden uitgevoerd, maar delen kan ook een knelpunt worden als er veel clients zijn of de clients samen een hoge belasting genereren. Aan elke verbinding is een I/O-thread gekoppeld. Bij het delen van de verbinding plaatsen de clients hun werk in de werkwachtrij van deze gedeelde I/O-thread en de voortgang van elke client is afhankelijk van de tijdige voltooiing van het werk in de wachtrij. De I/O-thread verwerkt de ingeklokte taken serieel. Als de I/O-threadwerkwachtrij van een gedeelde verbinding veel werk in behandeling heeft, dan zijn de symptomen vergelijkbaar met die van lage CPU-prestaties. Deze voorwaarde wordt beschreven in de vorige sectie over gelijktijdigheid, bijvoorbeeld clients die vastlopen, time-outs, verloren vergrendeling of vertraging in herstelpad.

Service Bus SDK maakt gebruik van het reactor-executor-* naamgevingspatroon voor de I/O-thread voor de verbinding. Wanneer de toepassing het knelpunt van de gedeelde verbinding ondervindt, kan dit worden weerspiegeld in het CPU-gebruik van de I/O-thread. In de heapdump of in het livegeheugen is het object ReactorDispatcher$workQueue ook de werkwachtrij van de I/O-thread. Een lange werkwachtrij in de geheugenmomentopname tijdens de knelpuntperiode kan erop wijzen dat de gedeelde I/O-thread overbelast is met werken die in behandeling zijn.

Als de belasting van de toepassing naar een Service Bus-eindpunt redelijk hoog is in termen van het totale aantal verzonden berichten of nettoladinggrootte, moet u daarom een afzonderlijk builder-exemplaar gebruiken voor elke client die u bouwt. Voor elke entiteit, wachtrij of onderwerp, kunt u bijvoorbeeld een nieuwe ServiceBusClientBuilder maken en er een client van bouwen. In het geval van extreem hoge belasting voor een specifieke entiteit, kunt u meerdere clientexemplaren voor die entiteit maken of clients uitvoeren op meerdere hosts, bijvoorbeeld containers of VM's, om de taakverdeling te verdelen.

Clients stoppen wanneer het aangepaste eindpunt van Application Gateway wordt gebruikt

Het aangepaste eindpuntadres verwijst naar een door de toepassing verstrekte HTTPS-eindpuntadres dat kan worden omgezet in Service Bus of geconfigureerd om verkeer naar Service Bus te routeren. Met Azure Application Gateway kunt u eenvoudig een HTTPS-front-end maken waarmee verkeer naar Service Bus wordt doorgestuurd. U kunt Service Bus SDK configureren voor een toepassing om een front-end-IP-adres van Application Gateway te gebruiken als het aangepaste eindpunt om verbinding te maken met Service Bus.

Application Gateway biedt verschillende beveiligingsbeleidsregels die verschillende TLS-protocolversies ondersteunen. Er zijn vooraf gedefinieerde beleidsregels die TLSv1.2 afdwingen als de minimale versie, er bestaan ook oude beleidsregels met TLSv1.0 als minimale versie. Op de HTTPS-front-end wordt een TLS-beleid toegepast.

Op dit moment herkent de Service Bus SDK bepaalde externe TCP-beëindigingen niet door de front-end van Application Gateway, die TLSv1.0 als minimale versie gebruikt. Als de front-end bijvoorbeeld TCP FIN verzendt, ACK-pakketten om de verbinding te sluiten wanneer de eigenschappen worden bijgewerkt, kan de SDK deze niet detecteren, zodat deze niet opnieuw verbinding maakt en clients geen berichten meer kunnen verzenden of ontvangen. Een dergelijke stop vindt alleen plaats wanneer u TLSv1.0 als minimale versie gebruikt. Als u dit wilt beperken, gebruikt u een beveiligingsbeleid met TLSv1.2 of hoger als de minimale versie voor de front-end van Application Gateway.

De ondersteuning voor TLSv1.0 en 1.1 in alle Azure-services wordt al aangekondigd eind 31 oktober 2024, dus de overgang naar TLSv1.2 wordt sterk aanbevolen.

Bericht- of sessievergrendeling gaat verloren

Een Service Bus-wachtrij of -onderwerpabonnement heeft een vergrendelingsduur ingesteld op resourceniveau. Wanneer de ontvangerclient een bericht uit de resource haalt, past de Service Bus-broker een eerste vergrendeling toe op het bericht. De initiële vergrendeling duurt zolang de vergrendeling op resourceniveau is ingesteld. Als de berichtvergrendeling niet wordt vernieuwd voordat het verloopt, geeft de Service Bus-broker het bericht vrij om het beschikbaar te maken voor andere ontvangers. Als de toepassing probeert een bericht te voltooien of te verlaten na het verlopen van de vergrendeling, mislukt de API-aanroep met de fout com.azure.messaging.servicebus.ServiceBusException: The lock supplied is invalid. Either the lock expired, or the message has already been removed from the queue.

De Service Bus-client ondersteunt het uitvoeren van een taak voor het vernieuwen van een achtergrondvergrendeling waarmee de berichtvergrendeling voortdurend wordt vernieuwd voordat deze verloopt. Standaard draait de vergrendelingsvernieuwingstaak gedurende 5 minuten. U kunt de verlengingsduur van de vergrendeling aanpassen met behulp van ServiceBusReceiverClientBuilder.maxAutoLockRenewDuration(Duration). Als u de Duration.ZERO waarde doorgeeft, wordt de taak voor het vernieuwen van vergrendeling uitgeschakeld.

In de volgende lijsten worden enkele van de gebruikspatronen of hostomgevingen beschreven die kunnen leiden tot een fout waarbij de vergrendeling is verbroken:

  • De taak voor het verlengen van vergrendeling is uitgeschakeld en de verwerkingstijd van het bericht van de toepassing overschrijdt de vergrendelingsduur die is ingesteld op resourceniveau.

  • De verwerkingstijd van het bericht van de toepassing overschrijdt de geconfigureerde tijd voor het vernieuwen van de vergrendeling. Houd er rekening mee dat als de duur van het vergrendelen niet expliciet is ingesteld, de standaardwaarde is ingesteld op 5 minuten.

  • De toepassing heeft de functie Prefetch ingeschakeld door de prefetch-waarde in te stellen op een positief geheel getal met behulp van ServiceBusReceiverClientBuilder.prefetchCount(prefetch). Wanneer de functie Prefetch is ingeschakeld, haalt de client het aantal berichten op dat gelijk is aan de prefetch uit de Service Bus-entiteit - wachtrij of onderwerp - en slaat deze op in de buffer voor in-memory prefetch. De berichten blijven in de prefetchbuffer staan totdat ze in de toepassing worden ontvangen. De client breidt de vergrendeling van de berichten niet uit terwijl ze zich in de prefetchbuffer bevinden. Als de verwerking van de toepassing zo lang duurt dat berichtvergrendelingen verlopen terwijl ze in de prefetchbuffer blijven, kan de toepassing de berichten verkrijgen met een verlopen vergrendeling. Zie Waarom is Prefetch niet de standaardoptie?

  • De hostomgeving heeft af en toe netwerkproblemen, bijvoorbeeld tijdelijke netwerkstoringen of storingen, die verhinderen dat de taak voor het vernieuwen van vergrendeling de vergrendeling op tijd verlengt.

  • De hostomgeving mist voldoende CPU's of heeft af en toe een tekort aan CPU-cycli waardoor de taak voor het verlengen van de vergrendeling op tijd wordt vertraagd.

  • De tijd van het hostsysteem is niet nauwkeurig, bijvoorbeeld de klok is scheef, waardoor de taak voor het verlengen van de vergrendeling wordt vertraagd en deze niet op tijd wordt uitgevoerd.

  • De I/O-thread van de verbinding is overbelast, wat van invloed is op de mogelijkheid om netwerkoproepen voor het vernieuwen van locks op tijd uit te voeren. De volgende twee scenario's kunnen dit probleem veroorzaken:

Het aantal vergrendelingsvernieuwingstaken in de client is gelijk aan de parameterwaarden maxMessages of maxConcurrentCalls die zijn ingesteld voor ServiceBusProcessorClient of ServiceBusReceiverClient.receiveMessages. Een groot aantal taken voor het vernieuwen van vergrendelingen die meerdere netwerkaanroepen doen, kan ook een nadelig effect hebben bij het beperken van de Service Bus-naamruimte.

Als de host onvoldoende resources heeft, kan de vergrendeling nog steeds verloren gaan, zelfs als er slechts enkele taken voor het vernieuwen van vergrendelingen worden uitgevoerd. Als u de Java-toepassing uitvoert in een container, raden we u aan twee of meer vCPU-kernen te gebruiken. We raden u niet aan om iets minder dan 1 vCPU-kern te selecteren bij het uitvoeren van Java-toepassingen in containeromgevingen. Zie Containeriseer uw Java-toepassingenvoor uitgebreide aanbevelingen over resourceplanning.

Dezelfde opmerkingen over vergrendelingen zijn ook relevant voor een Service Bus-wachtrij of een onderwerpabonnement waarvoor sessie is ingeschakeld. Wanneer de ontvangerclient verbinding maakt met een sessie in de resource, past de broker een eerste vergrendeling toe op de sessie. Als u de vergrendeling van de sessie wilt behouden, moet de taak voor het vernieuwen van de vergrendeling in de client de sessievergrendeling blijven vernieuwen voordat deze verloopt. Voor een resource waarvoor een sessie is ingeschakeld, worden de onderliggende partities soms verplaatst om taakverdeling te bereiken tussen Service Bus-knooppunten, bijvoorbeeld wanneer er nieuwe knooppunten worden toegevoegd om de belasting te delen. Als dat gebeurt, kunnen sessievergrendelingen verloren gaan. Als de toepassing probeert een bericht te voltooien of te verlaten nadat de sessievergrendeling is verbroken, mislukt de API-aanroep met de fout com.azure.messaging.servicebus.ServiceBusException: The session lock was lost. Request a new session receiver.

Upgraden naar 7.15.x of nieuwste

Als u problemen ondervindt, moet u deze eerst proberen op te lossen door een upgrade uit te voeren naar de nieuwste versie van de Service Bus SDK. Versie 7.15.x is een belangrijk herontwerp, waardoor langdurige prestatie- en betrouwbaarheidsproblemen worden opgelost.

Versie 7.15.x en hoger vermindert het hoppen van threads, verwijdert vergrendelingen, optimaliseert code in dynamische paden en vermindert geheugentoewijzingen. Deze wijzigingen resulteren in maximaal 45-50 keer hogere doorvoer op de ServiceBusProcessorClient.

Versie 7.15.x en hoger wordt ook geleverd met verschillende betrouwbaarheidsverbeteringen. Het behandelt verschillende racevoorwaarden (zoals prefetch en kredietberekeningen) en verbeterde foutafhandeling. Deze wijzigingen resulteren in een betere betrouwbaarheid in de aanwezigheid van tijdelijke problemen in verschillende clienttypen.

De nieuwste clients gebruiken

Het nieuwe onderliggende framework met deze verbeteringen, in versie 7.15.x en hoger, wordt de V2-Stack genoemd. Deze releaselijn bevat zowel de vorige generatie van de onderliggende stack- de stack die versie 7.14.x gebruikt - als de nieuwe V2-Stack.

Sommige clienttypen gebruiken standaard de V2-Stack, terwijl andere V2-Stack aanmelding vereisen. U kunt de opt-in of opt-out van een specifieke stack (V2 of de vorige generatie) voor een clienttype uitvoeren door com.azure.core.util.Configuration waarden op te geven wanneer u de client bouwt.

Voor een V2-Stack-sessie die wordt ontvangen met ServiceBusSessionReceiverClient moet u zich bijvoorbeeld aanmelden, zoals wordt weergegeven in het volgende voorbeeld:

ServiceBusSessionReceiverClient sessionReceiver = new ServiceBusClientBuilder()
    .connectionString(Config.CONNECTION_STRING)
    .configuration(new com.azure.core.util.ConfigurationBuilder()
        .putProperty("com.azure.messaging.servicebus.session.syncReceive.v2", "true") // 'false' by default, opt-in for V2-Stack.
        .build())
    .sessionReceiver()
    .queueName(Config.QUEUE_NAME)
    .buildClient();

De volgende tabel bevat de clienttypen en bijbehorende configuratienamen en geeft aan of de client momenteel standaard is ingeschakeld voor gebruik van de V2-Stack in de nieuwste versie 7.17.0. Voor een cliënt die zich niet standaard op de V2-Stack bevindt, kunt u het zojuist getoonde voorbeeld gebruiken om aan te melden.

Client-type Configuratienaam Is V2-Stack standaard ingeschakeld?
Afzender en beheerclient com.azure.messaging.servicebus.sendAndManageRules.v2 ja
Client voor niet-sessieprocessor en reactorontvanger com.azure.messaging.servicebus.nonSession.asyncReceive.v2 ja
Ontvangerclient voor sessieprocessor com.azure.messaging.servicebus.session.processor.asyncReceive.v2 ja
Sessiereactorontvangerclient com.azure.messaging.servicebus.session.reactor.asyncReceive.v2 ja
Niet-sessie synchrone ontvangerclient com.azure.messaging.servicebus.nonSession.syncReceive.v2 Nee
Sessiesynchrone ontvanger-client com.azure.messaging.servicebus.session.syncReceive.v2 Nee

Als alternatief voor het gebruik van com.azure.core.util.Configurationkunt u de opt-in of opt-out uitvoeren door dezelfde configuratienamen in te stellen met behulp van omgevingsvariabelen of systeemeigenschappen.

Volgende stappen

Als de richtlijnen voor probleemoplossing in dit artikel niet helpen bij het oplossen van problemen wanneer u de Azure SDK voor Java-clientbibliotheken gebruikt, raden we u aan een probleem op te slaan in de Azure SDK voor Java GitHub-opslagplaats.