Felsöka Azure Service Bus

Den här artikeln beskriver metoder för felundersökning, samtidighet och vanliga fel i Azure Service Bus Java-klientbiblioteket. Använd den här vägledningen för att identifiera grundorsaker och vidta åtgärder för att snabbare lösa problem med Azure Service Bus.

Aktivera och konfigurera loggning

Azure SDKs för Java erbjuder en konsekvent loggningshistoria som hjälper dig att felsöka programfel och för att påskynda lösningen. Loggarna som du skapar avbildar flödet för ett program innan du når terminaltillståndet för att hitta rotproblemet. Vägledning om loggning finns i Konfigurera loggning i Azure SDKs för Java och Felsökningsöversikt.

Förutom att aktivera loggning ger inställningen loggnivån till VERBOSE eller DEBUG insikter om bibliotekets tillstånd. I följande avsnitt visas exempelkonfigurationerna log4j2 och logback för att minska överdrivna meddelanden när utförlig loggning är aktiverad.

Konfigurera Log4J 2

Använd följande steg för att konfigurera Log4J 2:

  1. Lägg till beroendena i dinpom.xml med hjälp av dem från loggningsexemplet pom.xmli avsnittet "Beroenden som krävs för Log4j2".
  2. Lägg till log4j2.xml i mappen src/main/resources.

Konfigurera tillbakaloggning

Använd följande steg för att konfigurera tillbakaloggning:

  1. Lägg till beroendena i dinpom.xml med hjälp av dem från loggningsexemplet pom.xmli avsnittet "Beroenden som krävs för logback".
  2. Lägg till logback.xml i mappen src/main/resources.

Aktivera AMQP-transportloggning

Om det inte räcker med att aktivera klientloggning för att diagnostisera dina problem kan du aktivera loggning till en fil i det underliggande AMQP-biblioteket Qpid Proton-J-. Qpid Proton-J använder java.util.logging. Du kan aktivera loggning genom att skapa en konfigurationsfil med innehållet som visas i nästa avsnitt. Du kan också ange proton.trace.level=ALL och vilka konfigurationsalternativ du vill för java.util.logging.Handler implementeringen. Implementeringsklasserna och deras alternativ finns i Package java.util.logging i Java 8 SDK-dokumentationen.

Om du vill spåra AMQP-transportramarna anger du miljövariabeln PN_TRACE_FRM=1.

Exempelfil för logging.properties

Följande konfigurationsfil loggar TRACE-nivåutdata från Proton-J till filen 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

Minska loggning

Ett sätt att minska loggningen är att ändra verbositeten. Ett annat sätt är att lägga till filter som exkluderar loggar från loggningsnamnspaket som com.azure.messaging.servicebus eller com.azure.core.amqp. Exempel finns i XML-filerna i avsnitten Konfigurera Log4J 2 och Konfigurera loggback.

När du skickar en bugg är loggmeddelandena från klasser i följande paket intressanta:

  • com.azure.core.amqp.implementation
  • com.azure.core.amqp.implementation.handler
    • Undantaget är att du kan ignorera meddelandet onDelivery i ReceiveLinkHandler.
  • com.azure.messaging.servicebus.implementation

Samtidighet i ServiceBusProcessorClient

ServiceBusProcessorClient gör att du kan konfigurera hur många anrop till meddelandehanteraren som sker samtidigt. Den här konfigurationen gör det möjligt att bearbeta flera meddelanden parallellt. För en ServiceBusProcessorClient som använder meddelanden från en entitet utan session kan du konfigurera önskad samtidighet genom att använda API:et maxConcurrentCalls. För en sessionsaktiverad entitet är den önskade samtidigheten maxConcurrentSessions gånger maxConcurrentCalls.

Om du ser färre samtidiga anrop till meddelandehanteraren än den konfigurerade samtidigheten kan det bero på att trådpoolen inte har rätt storlek.

ServiceBusProcessorClient använder daemontrådar från den globala boundedElastic trådpoolen för att anropa meddelandehanteraren. Det maximala antalet samtidiga trådar i den här poolen begränsas av ett tak. Som standard är det här taket tio gånger så många tillgängliga CPU-kärnor. För att ServiceBusProcessorClient effektivt ska kunna stödja programmets önskade samtidighet (maxConcurrentCalls eller maxConcurrentSessions gånger maxConcurrentCalls) måste du ha ett boundedElastic pooltaksvärde som är högre än den önskade samtidigheten. Du kan åsidosätta standardtaket genom att ange systemegenskapen reactor.schedulers.defaultBoundedElasticSize.

Justera trådpoolen och CPU-allokeringen från fall till fall. Men när du åsidosätter pooltaket begränsar du som utgångspunkt de samtidiga trådarna till cirka 20–30 per CPU-kärna. Begränsa den önskade samtidigheten per ServiceBusProcessorClient instans till cirka 20–30. Profilera och mät ditt specifika användningsfall och justera samtidighetsaspekterna i enlighet med detta. För scenarier med hög belastning bör du överväga att köra flera ServiceBusProcessorClient instanser där varje instans skapas från en ny ServiceBusClientBuilder instans. Överväg också att köra varje ServiceBusProcessorClient på en dedikerad värd , till exempel en container eller virtuell dator, så att driftstopp i en värd inte påverkar den övergripande meddelandebearbetningen.

Tänk på att det har negativa effekter att ange ett högt värde för pooltaket på en värd med få CPU-kärnor. Vissa tecken på låg CPU-resurser eller en pool med för många trådar på färre processorer är: frekventa timeouter, förlorade lås, dödläge eller lägre dataflöde. Om du kör Java-programmet på en container använder du två eller flera vCPU-kärnor. Välj inte något mindre än 1 vCPU-kärna när du kör Java program i containerbaserade miljöer. Detaljerade rekommendationer om resurstilldelning finns i Containerize your Java applications.

Flaskhals för anslutningsdelning

Alla klienter som du skapar från en delad ServiceBusClientBuilder instans delar samma anslutning till Service Bus namnområdet.

Att använda en delad anslutning möjliggör multiplexeringsåtgärder mellan klienter på en anslutning, men delning kan också bli en flaskhals om det finns många klienter, eller om klienterna tillsammans genererar hög belastning. Varje anslutning har en I/O-tråd associerad med den. När du delar en anslutning placerar klienterna sitt arbete i den här delade I/O-trådens arbetskö och förloppet för varje klient beror på hur snabbt arbetet i kön har slutförts. I/O-tråden hanterar det kodade arbetet seriellt. Det vill säga, om I/O-trådens arbetskö för en delad anslutning får många väntande arbetsuppgifter att hantera, liknar symtomen dem vid låg CPU-belastning. Det här villkoret beskrivs i föregående avsnitt om samtidighet – till exempel klienter som stannar upp, tidsgräns, förlorat lås eller långsammare återställningssökväg.

Service Bus SDK använder reactor-executor-* namnmönster för anslutnings-I/O-tråden. När programmet upplever flaskhalsen för delad anslutning kan det återspeglas i I/O-trådens CPU-användning. I heapdumparen eller i realtidsminnet är objektet ReactorDispatcher$workQueue också arbetskön för I/O-tråden. En lång arbetskö i minnesdumpen under flaskhalsperioden kan tyda på att den delade I/O-tråden är överbelastad med väntande uppgifter.

Om belastningen från programmet på en Service Bus-slutpunkt är relativt hög, sett till det totala antalet skickade och mottagna meddelanden eller nyttolastens storlek, bör du därför använda en separat builder-instans för varje klient som du skapar. För varje entitet – kö eller ämne – kan du till exempel skapa en ny ServiceBusClientBuilder och skapa en klient från den. Vid extremt hög belastning på en viss entitet kanske du antingen vill skapa flera klientinstanser för den entiteten eller köra klienter på flera värdar , till exempel containrar eller virtuella datorer, för att belastningsutjämning.

Klienter får stopp vid användning av en anpassad slutpunkt för Application Gateway

Den anpassade slutpunktsadressen avser en HTTPS-slutpunktsadress som tillhandahålls av programmet och som du kan matcha mot Service Bus eller konfigurera för att dirigera trafik till Service Bus. Azure Application Gateway gör det enkelt att skapa en HTTPS-klientdel som vidarebefordrar trafik till Service Bus. Du kan konfigurera Service Bus SDK för ett program att använda en Application Gateway-klientdels-IP-adress som anpassad slutpunkt för att ansluta till Service Bus.

Application Gateway erbjuder flera säkerhetsprinciper som stöder olika TLS-protokollversioner. Det finns fördefinierade principer som tillämpar TLS 1.2 som lägsta version och det finns äldre principer som använder TLS 1.0 som lägsta version. Du tillämpar en TLS-princip på HTTPS-klientdelen.

För närvarande känner Service Bus SDK inte igen vissa fjärr-TCP-avslutningar av Application Gateway-klientdelen, som använder TLS 1.0 som lägsta version. Om klientdelen till exempel skickar TCP FIN- och ACK-paket för att stänga anslutningen när den uppdaterar sina egenskaper kan SDK:t inte identifiera dessa paket. Därför återansluts den inte och klienter kan inte skicka eller ta emot meddelanden längre. Det här stoppet sker bara när du använder TLS 1.0 som lägsta version. Du kan åtgärda det här problemet genom att använda en säkerhetsprincip som anger TLS 1.2 eller högre som lägsta version för Application Gateway-klientdelen.

Stöd för TLS 1.0 och 1.1 för alla Azure tjänster har redan meddelats att upphöra den 31 oktober 2024, så övergången till TLS 1.2.

Meddelande- eller sessionslåset går förlorat

En Service Bus-kö eller ämnesprenumeration har en låsvaraktighet som är inställd på resursnivå. När mottagarklienten hämtar ett meddelande från resursen tillämpar Service Bus-koordinatorn ett första lås på meddelandet. Det initiala låset varar under den låsvaraktighet som angetts på resursnivå. Om meddelandelåset inte förnyas innan det upphör att gälla släpper Service Bus broker meddelandet för att göra det tillgängligt för andra mottagare. Om programmet försöker slutföra eller avbryta ett meddelande efter att låset upphört att gälla misslyckas API-anropet med felet com.azure.messaging.servicebus.ServiceBusException: The lock supplied is invalid. Either the lock expired, or the message has already been removed from the queue.

Service Bus-klienten har stöd för att köra en förnyelseaktivitet för bakgrundslås som förnyar meddelandelåset kontinuerligt varje gång innan det upphör att gälla. Som standard körs uppgiften för låsförnyelse i 5 minuter. Du kan justera låsets förnyelsetid med hjälp av ServiceBusReceiverClientBuilder.maxAutoLockRenewDuration(Duration). Om du skickar Duration.ZERO-värdet inaktiveras låsförnyingsaktiviteten.

Följande lista beskriver några användningsmönster eller värdmiljöer som kan leda till felet att låset förloras:

  • Aktiviteten för att förnya låset är inaktiverad och programmets meddelandebearbetningstid överskrider den låsvaraktighet som angetts på resursnivå.

  • Applikationens meddelandebearbetningstid överskrider den konfigurerade varaktigheten för låsförnyelseuppgiften. Observera att om förnyelsevaraktigheten för låset inte anges uttryckligen är standardvärdet 5 minuter.

  • Programmet aktiverar funktionen Prefetch genom att ange prefetch-värdet till ett positivt heltal med hjälp av ServiceBusReceiverClientBuilder.prefetchCount(prefetch). När prefetch-funktionen är aktiverad hämtar klienten antalet meddelanden som är lika med prefetch från den Service Bus entiteten – kö eller ämne – och lagrar dem i minnesintern förinläsningsbuffert. Meddelandena finns kvar i prefetch-bufferten tills de tas emot i programmet. Klienten utökar inte låset för meddelandena medan de är i prefetch-bufferten. Om programbearbetningen tar så lång tid att meddelandelåset upphör att gälla medan det finns kvar i prefetch-bufferten kan programmet hämta meddelandena med ett utgånget lås. Mer information finns i Varför är Prefetch inte standardalternativet?

  • Värdmiljön har tillfälliga nätverksproblem – till exempel tillfälligt nätverksfel eller avbrott – som hindrar låsförnyelseaktiviteten från att förnya låset i tid.

  • Värdmiljön saknar tillräckligt med processorer eller har brist på CPU-cykler tillfälligt som fördröjer låsförnyingsaktiviteten från att köras i tid.

  • Värdsystemtiden är inte korrekt – till exempel är klockan felinställd – vilket fördröjer låsförnyelseuppgiften och hindrar den från att köras enligt schemat.

  • Anslutnings-I/O-tråden är överbelastad, vilket påverkar dess förmåga att förnya lås genom nätverksanrop i tid. Följande två scenarier kan orsaka det här problemet:

    • Programmet kör för många mottagarklienter som delar samma anslutning. Mer information finns i avsnittet Flaskhals för anslutningsdelning.
    • Programmet konfigurerar ServiceBusReceiverClient.receiveMessages eller ServiceBusProcessorClient att ha stora maxMessages- eller maxConcurrentCalls-värden. Mer information finns i avsnittet Concurrency i ServiceBusProcessorClient.
  • Ett vanligt programmönster som ökar sannolikheten för ett fel där låset förloras innebär att schemalägga långvariga uppgifter för förnyelse av lås – till exempel uppgifter med varaktigheter som sträcker sig över flera timmar. Som tidigare nämnts kan olika faktorer utanför en Service Bus-klients kontroll störa lyckad låsförnyelse, så programdesign bör undvika att förutsätta garanterad förnyelse under längre perioder. Om du vill undvika att behöva bearbeta långvariga åtgärder kan du överväga att dela upp arbetet i mindre segment eller implementera idempotent kontrollpunktslogik.

Antalet aktiviteter för att förnya lås i klienten är lika med de maxMessages- eller maxConcurrentCalls parametervärden som angetts för ServiceBusProcessorClient eller ServiceBusReceiverClient.receiveMessages. Ett stort antal aktiviteter för att förnya låset som gör flera nätverksanrop kan också ha en negativ effekt vid begränsning av Service Bus-namnområde.

Om värden inte har tillräckliga resurser kan låset fortfarande förloras även om det bara körs ett fåtal aktiviteter för låsförnyelse. Om du kör Java-programmet på en container använder du två eller flera vCPU-kärnor. Välj inte något mindre än 1 vCPU-kärna när du kör Java program i containerbaserade miljöer. Detaljerade rekommendationer om resurstilldelning finns i Containerize your Java applications.

Samma anmärkningar om lås är också relevanta för en Service Bus-kö eller en ämnesprenumeration som har session aktiverad. När mottagarklienten ansluter till en session i resursen, tillämpar mäklaren ett initialt lås på sessionen. För att behålla låset på sessionen måste klienten fortsätta att förnya sessionslåset innan det går ut. För en sessionsaktiverad resurs flyttas de underliggande partitionerna ibland för att uppnå belastningsutjämning mellan Service Bus-noder, till exempel när nya noder läggs till för att dela belastningen. När det händer kan sessionslås gå förlorade. Om programmet försöker slutföra eller avbryta ett meddelande när sessionslåset har förlorats misslyckas API-anropet med felet com.azure.messaging.servicebus.ServiceBusException: The session lock was lost. Request a new session receiver.

Nästa steg

Om felsökningsguiden i den här artikeln inte hjälper dig att lösa problem när du använder Azure SDKs för Java klientbibliotek kan du ange ett problem i Azure SDKs för Java GitHub lagringsplats.