Behandeln von Problemen bei der Verwendung des Azure Cosmos DB Async Java SDK v2 mit „API für NoSQL“-Konten
GILT FÜR: NoSQL
Wichtig
Dies ist nicht das neueste Java SDK für Azure Cosmos DB! Sie sollten ein Upgrade Ihres Projekts auf das Azure Cosmos DB Java SDKv4 durchführen und dann den Leitfaden zur Problembehandlung für das Azure Cosmos DB Java SDK v4 lesen. Befolgen Sie für ein Upgrade die Anweisungen in den Anleitungen Migrieren zum Azure Cosmos DB Java SDK v4 und Gegenüberstellung von Reactor und RxJava.
In diesem Artikel wird nur die Problembehandlung für das Azure Cosmos DB Async Java SDK v2 behandelt. Weitere Informationen finden Sie in den Versionshinweisen zum Azure Cosmos DB Async Java SDK v2, im Maven-Repository und in den Leistungstipps.
Wichtig
Am 31. August 2024 wird das Async Java SDK v2.x von Azure Cosmos DB eingestellt. Das SDK und alle Anwendungen, die es verwenden, sind weiterhin funktionsfähig. Azure Cosmos DB stellt jedoch keine weitere Wartung und Unterstützung für dieses SDK bereit. Es wird empfohlen, die obigen Anweisungen zur Migration zum Azure Cosmos DB Java SDK v4 zu befolgen.
In diesem Artikel werden allgemeine Probleme, Problemumgehungen, Diagnoseschritte und Tools für die Verwendung des Java Async SDK mit Azure Cosmos DB für NoSQL-Konten behandelt. Das Java Async SDK bietet eine clientseitige logische Darstellung für den Zugriff auf Azure Cosmos DB for NoSQL. Dieser Artikel beschreibt hilfreiche Tools und Vorgehensweisen für Problemfälle.
Beginnen Sie mit dieser Liste:
- Sehen Sie sich den Abschnitt Häufig auftretende Probleme und Problemumgehungen in diesem Artikel an.
- Sehen Sie sich das SDK an (verfügbar als Open-Source-Lösung auf GitHub). Es verfügt über einen aktiv überwachten Problemabschnitt. Überprüfen Sie, ob Sie ein ähnliches Problem finden, für das bereits eine Problemumgehung dokumentiert wurde.
- Überprüfen Sie die Leistungstipps, und implementieren Sie die Empfehlungen.
- Lesen Sie den Rest dieses Artikels, falls Sie keine Lösung gefunden haben. Reichen Sie anschließend ein GitHub-Problem ein.
Häufig auftretende Probleme und Problemumgehungen
Netzwerkprobleme, Netty-Lesetimeoutfehler, niedriger Durchsatz, hohe Latenz
Allgemeine Empfehlungen
- Vergewissern Sie sich, dass die App in der gleichen Region ausgeführt wird, in der sich auch Ihr Azure Cosmos DB-Konto befindet.
- Überprüfen Sie die CPU-Auslastung auf dem Host, auf dem die App ausgeführt wird. Wenn die CPU-Auslastung 90 Prozent oder mehr beträgt, führen Sie Ihre App auf einem Host mit einer höheren Konfiguration aus. Alternativ können Sie die Last auf mehrere Computer verteilen.
Verbindungsdrosselung
Eine Verbindungsdrosselung kann entweder aufgrund eines Verbindungslimit auf einem Hostcomputer oder aufgrund von Azure SNAT-Portauslastung (PAT) auftreten.
Verbindungslimit auf einem Hostcomputer
Bei einigen Linux-Systemen (beispielsweise Red Hat) gilt eine Obergrenze für die Gesamtzahl geöffneter Dateien. Da Sockets in Linux als Dateien implementiert werden, schränkt dies auch die Gesamtanzahl von Verbindungen ein. Führen Sie den folgenden Befehl aus.
ulimit -a
Die maximal zulässige Anzahl geöffneter Dateien (nofile) muss mindestens doppelt so hoch sein wie die Größe Ihres Verbindungspools. Weitere Informationen finden Sie unter Leistungstipps für Azure Cosmos DB und Async Java.
Azure SNAT-Portauslastung (PAT)
Wenn Ihre App auf einem virtuellen Azure-Computer ohne öffentliche IP-Adresse bereitgestellt wird, werden standardmäßig Azure SNAT-Ports verwendet, um Verbindungen mit beliebigen Endpunkten außerhalb Ihres virtuellen Computers herzustellen. Die Anzahl zulässiger Verbindungen des virtuellen Computers mit dem Azure Cosmos DB-Endpunkt wird durch die Azure SNAT-Konfiguration eingeschränkt.
Azure SNAT-Ports werden nur verwendet, wenn Ihr virtueller Computer eine private IP-Adresse besitzt und ein Prozess auf dem virtuellen Computer versucht, eine Verbindung mit einer öffentlichen IP-Adresse herzustellen. Es gibt zwei Problemumgehungen, um die Einschränkung durch Azure SNAT zu vermeiden:
Fügen Sie Ihren Azure Cosmos DB-Dienstendpunkt dem Subnetz Ihres virtuellen Netzwerks von Azure Virtual Machines hinzu. Weitere Informationen finden Sie unter Azure Virtual Network-Dienstendpunkte.
Wenn der Dienstendpunkt aktiviert ist, werden die Anforderungen nicht mehr von einer öffentlichen IP-Adresse an Azure Cosmos DB gesendet. Stattdessen wird die Identität des virtuellen Netzwerks und des Subnetzes gesendet. Diese Änderung kann zu Firewallproblemen führen, wenn nur öffentliche IP-Adressen zulässig sind. Wenn Sie eine Firewall verwenden und den Dienstendpunkt aktivieren, fügen Sie der Firewall mithilfe von VNET-ACLs ein Subnetz hinzu.
Weisen Sie Ihrem virtuellen Azure-Computer eine öffentliche IP-Adresse zu.
Dienst nicht erreichbar – Firewall
ConnectTimeoutException
gibt an, dass das SDK den Dienst nicht erreichen kann.
Im direkten Modus erhalten Sie möglicherweise einen Fehler ähnlich dem folgenden:
GoneException{error=null, resourceAddress='https://cdb-ms-prod-westus-fd4.documents.azure.com:14940/apps/e41242a5-2d71-5acb-2e00-5e5f744b12de/services/d8aa21a5-340b-21d4-b1a2-4a5333e7ed8a/partitions/ed028254-b613-4c2a-bf3c-14bd5eb64500/replicas/131298754052060051p//', statusCode=410, message=Message: The requested resource is no longer available at the server., getCauseInfo=[class: class io.netty.channel.ConnectTimeoutException, message: connection timed out: cdb-ms-prod-westus-fd4.documents.azure.com/101.13.12.5:14940]
Wenn Sie auf dem Computer mit der App eine Firewall ausführen, öffnen Sie den Portbereich 10.000 bis 20.000, der im direkten Modus verwendet wird. Halten Sie auch das Verbindungslimit auf einem Hostcomputer ein.
HTTP-Proxy
Wenn Sie einen HTTP-Proxy verwenden, vergewissern Sie sich, dass er die Anzahl von Verbindungen unterstützt, die in ConnectionPolicy
des SDK konfiguriert ist.
Andernfalls treten Verbindungsprobleme auf.
Ungültiges Codierungsmuster: Blockieren eines Netty E/A-Threads
Das SDK verwendet für die Kommunikation mit Azure Cosmos DB die Netty-E/A-Bibliothek. Das SDK verfügt über asynchrone APIs und verwendet nicht blockierende E/A-APIs von Netty. Die E/A-Aufgaben des SDK werden in Netty-E/A-Threads ausgeführt. Die Anzahl von Netty-E/A-Threads ist so konfiguriert, dass sie mit der Anzahl von CPU-Kernen des App-Computers übereinstimmt.
Die Netty-E/A-Threads sind nur für nicht blockierende Netty-E/A-Aufgaben vorgesehen. Das SDK gibt das API-Aufrufergebnis für einen der Netty-E/A-Threads an den Code der App zurück. Wenn die App nach dem Empfang von Ergebnissen im Netty-Thread einen länger dauernden Vorgang ausführt, stehen dem SDK möglicherweise nicht genügend E/A-Threads für interne E/A-Aufgaben zur Verfügung. Eine solche App-Programmierung kann zu niedrigem Durchsatz, hoher Wartezeit und zu io.netty.handler.timeout.ReadTimeoutException
-Fehlern führen. Wechseln Sie zur Problemumgehung den Thread, wenn Sie wissen, dass der Vorgang länger dauert.
Sehen Sie sich beispielsweise den folgenden Codeausschnitt an. Möglicherweise wird im Netty-Thread eine Aufgabe ausgeführt, die mehr als nur ein paar Millisekunden dauert. In diesem Fall kann es vorkommen, dass für die Verarbeitung von E/A-Aufgaben kein Netty-E/A-Thread mehr vorhanden ist. Dies führt dann zu einem ReadTimeoutException-Fehler.
Async Java SDK v2 (Maven com.microsoft.azure::azure-cosmosdb)
@Test
public void badCodeWithReadTimeoutException() throws Exception {
int requestTimeoutInSeconds = 10;
ConnectionPolicy policy = new ConnectionPolicy();
policy.setRequestTimeoutInMillis(requestTimeoutInSeconds * 1000);
AsyncDocumentClient testClient = new AsyncDocumentClient.Builder()
.withServiceEndpoint(TestConfigurations.HOST)
.withMasterKeyOrResourceToken(TestConfigurations.MASTER_KEY)
.withConnectionPolicy(policy)
.build();
int numberOfCpuCores = Runtime.getRuntime().availableProcessors();
int numberOfConcurrentWork = numberOfCpuCores + 1;
CountDownLatch latch = new CountDownLatch(numberOfConcurrentWork);
AtomicInteger failureCount = new AtomicInteger();
for (int i = 0; i < numberOfConcurrentWork; i++) {
Document docDefinition = getDocumentDefinition();
Observable<ResourceResponse<Document>> createObservable = testClient
.createDocument(getCollectionLink(), docDefinition, null, false);
createObservable.subscribe(r -> {
try {
// Time-consuming work is, for example,
// writing to a file, computationally heavy work, or just sleep.
// Basically, it's anything that takes more than a few milliseconds.
// Doing such operations on the IO Netty thread
// without a proper scheduler will cause problems.
// The subscriber will get a ReadTimeoutException failure.
TimeUnit.SECONDS.sleep(2 * requestTimeoutInSeconds);
} catch (Exception e) {
}
},
exception -> {
//It will be io.netty.handler.timeout.ReadTimeoutException.
exception.printStackTrace();
failureCount.incrementAndGet();
latch.countDown();
},
() -> {
latch.countDown();
});
}
latch.await();
assertThat(failureCount.get()).isGreaterThan(0);
}
Ändern Sie zur Problemumgehung den Thread für die Ausführung zeitaufwendiger Aufgaben. Definieren Sie eine Singletoninstanz des Schedulers für Ihre App.
Async Java SDK v2 (Maven com.microsoft.azure::azure-cosmosdb)
// Have a singleton instance of an executor and a scheduler.
ExecutorService ex = Executors.newFixedThreadPool(30);
Scheduler customScheduler = rx.schedulers.Schedulers.from(ex);
Unter Umständen müssen zeitaufwendige Aufgaben (etwa rechenintensive Aufgaben oder blockierende E/A-Vorgänge) ausgeführt werden. Verlagern Sie den Thread in diesem Fall mithilfe der API .observeOn(customScheduler)
auf einen durch customScheduler
bereitgestellten Worker.
Async Java SDK v2 (Maven com.microsoft.azure::azure-cosmosdb)
Observable<ResourceResponse<Document>> createObservable = client
.createDocument(getCollectionLink(), docDefinition, null, false);
createObservable
.observeOn(customScheduler) // Switches the thread.
.subscribe(
// ...
);
Durch die Verwendung von observeOn(customScheduler)
geben Sie den Netty-E/A-Thread frei und wechseln zu Ihrem eigenen benutzerdefinierten Thread, der vom benutzerdefinierten Scheduler bereitgestellt wird.
Diese Änderung löst das Problem. Es treten keine Fehler vom Typ io.netty.handler.timeout.ReadTimeoutException
mehr auf.
Problem „Verbindungspool erschöpft“
PoolExhaustedException
ist ein clientseitiger Fehler. Dieser Fehler deutet darauf hin, dass Ihre App-Workload die Kapazitäten des SDK-Verbindungspools übersteigt. Erhöhen Sie die Größe des Verbindungspools, oder verteilen Sie die Last auf mehrere Apps.
Anforderungsrate zu groß
Hierbei handelt es sich um einen serverseitigen Fehler. Er gibt an, dass Sie den bereitgestellten Durchsatz verbraucht haben. Wiederholen Sie den Vorgang zu einem späteren Zeitpunkt. Sollte dieser Fehler häufiger auftreten, empfiehlt sich unter Umständen eine Erhöhung des Sammlungsdurchsatzes.
Fehler beim Herstellen einer Verbindung mit dem Azure Cosmos DB-Emulator
Das HTTPS-Zertifikat des Azure Cosmos DB-Emulators ist selbstsigniert. Importieren Sie das Emulatorzertifikat in eine Java TrustStore-Instanz, damit das SDK mit dem Emulator verwendet werden kann. Weitere Informationen finden Sie unter Exportieren der Azure Cosmos DB-Emulatorzertifikate.
Probleme durch Abhängigkeitskonflikte
Exception in thread "main" java.lang.NoSuchMethodError: rx.Observable.toSingle()Lrx/Single;
Die oben genannten Ausnahme legt nahe, dass bei Ihnen eine Abhängigkeit von einer älteren Version der RxJava-Bibliothek (z. B. 1.2.2) vorhanden ist. Unser SDK verwendet RxJava 1.3.8, die APIs enthält, die in früheren Version von RxJava nicht verfügbar sind.
Die Problemumgehung für solche Probleme besteht darin, zu identifizieren, in welchen anderen Abhängigkeiten RxJava-1.2.2 vorhanden ist, die transitive Abhängigkeit von RxJava-1.2.2 auszuschließen und dem CosmosDB SDK zu gestatten, die neuere Version einzubringen.
Um zu identifizieren, welche Bibliothek RxJava-1.2.2 einbringt, führen Sie den folgenden Befehl neben Ihrer Projektdatei „pom.xml“ aus:
mvn dependency:tree
Weitere Informationen finden Sie in der Anleitung zur Maven-Abhängigkeitsstruktur.
Sobald Sie festgestellt haben, dass RxJava-1.2.2 eine transitive Abhängigkeit von einer anderen Abhängigkeit Ihres Projekts ist, können Sie die Abhängigkeit von dieser Bibliothek in Ihrer pom-Datei ändern und die transitive Abhängigkeit von RxJava ausschließen:
<dependency>
<groupId>${groupid-of-lib-which-brings-in-rxjava1.2.2}</groupId>
<artifactId>${artifactId-of-lib-which-brings-in-rxjava1.2.2}</artifactId>
<version>${version-of-lib-which-brings-in-rxjava1.2.2}</version>
<exclusions>
<exclusion>
<groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId>
</exclusion>
</exclusions>
</dependency>
Weitere Informationen finden Sie in der Anleitung zum Ausschließen transitiver Abhängigkeiten.
Aktivieren von Client-SDK-Protokollierung
Das Java Async SDK verwendet SLF4j als Protokollierungsfassade, die die Anmeldung bei gängigen Protokollierungsframeworks wie log4j und logback unterstützt.
Wenn Sie beispielsweise log4j als Protokollierungsframework verwenden möchten, fügen Sie Ihrem Java-Klassenpfad die folgenden Bibliotheken hinzu:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
Fügen Sie außerdem eine log4j-Konfiguration hinzu:
# this is a sample log4j configuration
# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=INFO, A1
log4j.category.com.microsoft.azure.cosmosdb=DEBUG
#log4j.category.io.netty=INFO
#log4j.category.io.reactivex=INFO
# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender
# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d %5X{pid} [%t] %-5p %c - %m%n
Weitere Informationen finden Sie im Leitfaden zur sfl4j-Protokollierung.
Netzwerkstatistiken des Betriebssystems
Führen Sie den Befehl „netstat“ aus, um einen Überblick darüber zu erhalten, wie viele Verbindungen sich im Zustand ESTABLISHED
, CLOSE_WAIT
usw. befinden.
Unter Linux können Sie den folgenden Befehl ausführen:
netstat -nap
Filtern Sie das Ergebnis so, dass nur Verbindungen mit dem Azure Cosmos DB-Endpunkt angezeigt werden.
Die Anzahl von Verbindungen mit dem Azure Cosmos DB-Endpunkt im Zustand ESTABLISHED
kann nicht höher sein als die Größe des konfigurierten Verbindungspools.
Viele Verbindungen mit dem Azure Cosmos DB-Endpunkt befinden sich möglicherweise im Zustand CLOSE_WAIT
. Vielleicht sind es sogar mehr als 1.000. Eine so hohe Zahl deutet darauf hin, dass Verbindungen sehr schnell hergestellt und wieder getrennt werden. Das führt unter Umständen zu Problemen. Weitere Informationen finden Sie im Abschnitt Häufig auftretende Probleme und Problemumgehungen.