Řešení potíží při použití asynchronní sady Java SDK služby Azure Cosmos DB v2 s účty API for NoSQL

PLATÍ PRO: NoSQL

Důležité

Nejedná se o nejnovější sadu Java SDK pro službu Azure Cosmos DB! Projekt byste měli upgradovat na sadu Java SDK služby Azure Cosmos DB v4 a pak si přečíst průvodce odstraňováním potíží se sadou Java SDK služby Azure Cosmos DB v4. Postupujte podle pokynů v průvodci migrací na sadu Java SDK služby Azure Cosmos DB verze 4 a průvodce upgradem nástroje Reactor vs RxJava .

Tento článek popisuje řešení potíží pouze se sadou Azure Cosmos DB Async Java SDK verze 2. Další informace najdete v poznámkách k verzi sady Azure Cosmos DB Async Java SDK v2, úložišti Maven a tipy k výkonu.

Důležité

31. srpna 2024 bude sada Azure Cosmos DB Async Java SDK v2.x vyřazena; sada SDK a všechny aplikace používající sadu SDK budou i nadále fungovat; Azure Cosmos DB jednoduše přestane poskytovat další údržbu a podporu pro tuto sadu SDK. Pokud chcete migrovat na sadu Java SDK služby Azure Cosmos DB verze 4, doporučujeme postupovat podle výše uvedených pokynů.

Tento článek se zabývá běžnými problémy, alternativními řešeními, diagnostickými kroky a nástroji při použití sady Java Async SDK s účty Azure Cosmos DB for NoSQL. Sada Java Async SDK poskytuje logickou reprezentaci na straně klienta pro přístup ke službě Azure Cosmos DB for NoSQL. Tento článek popisuje nástroje a přístupy, které vám pomůžou v případě jakýchkoli problémů.

Začněte tímto seznamem:

Běžné problémy a alternativní řešení

Problémy se sítí, selhání časového limitu čtení Netty, nízká propustnost, vysoká latence

Obecné návrhy

  • Ujistěte se, že je aplikace spuštěná ve stejné oblasti jako váš účet služby Azure Cosmos DB.
  • Zkontrolujte využití procesoru na hostiteli, na kterém je aplikace spuštěná. Pokud je využití procesoru 90 procent nebo více, spusťte aplikaci na hostiteli s vyšší konfigurací. Nebo můžete zatížení distribuovat na více počítačích.

omezování Připojení

Připojení omezování může dojít z důvodu limitu připojení na hostitelském počítači nebo vyčerpání portů AZURE SNAT (PAT).

omezení Připojení na hostitelském počítači

Některé systémy Linux, například Red Hat, mají horní limit celkového počtu otevřených souborů. Sokety v Linuxu se implementují jako soubory, takže toto číslo omezuje také celkový počet připojení. Spusťte následující příkaz:

ulimit -a

Maximální povolený počet povolených otevřených souborů, které jsou označené jako "nofile", musí být alespoň dvojnásobná velikost fondu připojení. Další informace najdete v tématu Tipy k výkonu.

Vyčerpání portů AZURE SNAT (PAT)

Pokud je vaše aplikace nasazená ve službě Azure Virtual Machines bez veřejné IP adresy, navazují ve výchozím nastavení porty Azure SNAT připojení k libovolnému koncovému bodu mimo váš virtuální počítač. Počet připojení povolených z virtuálního počítače ke koncovému bodu služby Azure Cosmos DB je omezený konfigurací Azure SNAT.

Porty Azure SNAT se používají jenom v případech, kdy má váš virtuální počítač privátní IP adresu a proces z virtuálního počítače se pokusí připojit k veřejné IP adrese. Existují dvě alternativní řešení, jak se vyhnout omezení Azure SNAT:

  • Přidejte koncový bod služby Azure Cosmos DB do podsítě virtuální sítě Azure Virtual Machines. Další informace najdete v tématu Koncové body služby Azure Virtual Network.

    Když je koncový bod služby povolený, požadavky se už do Azure Cosmos DB neposílají z veřejné IP adresy. Místo toho se odešle identita virtuální sítě a podsítě. Tato změna může vést k poklesu brány firewall, pokud jsou povoleny pouze veřejné IP adresy. Pokud používáte bránu firewall, když povolíte koncový bod služby, přidejte do brány firewall podsíť pomocí seznamů ACL virtuální sítě.

  • Přiřaďte virtuálnímu počítači Azure veřejnou IP adresu.

Nejde se spojit se službou – brána firewall

ConnectTimeoutException značí, že sada SDK se nemůže spojit se službou. Při použití přímého režimu může dojít k selhání podobné tomuto:

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]

Pokud je na počítači s aplikací spuštěná brána firewall, otevřete rozsah portů 10 000 až 20 000, který se používá v přímém režimu. Také postupujte podle limitu Připojení na hostitelském počítači.

Proxy server HTTP

Pokud používáte proxy server HTTP, ujistěte se, že podporuje počet připojení nakonfigurovaných v sadě SDK ConnectionPolicy. V opačném případě dochází k problémům s připojením.

Neplatný vzor kódování: Blokování vlákna vstupně-výstupních operací Netty

Sada SDK používá ke komunikaci se službou Azure Cosmos DB knihovnu Netty IO. Sada SDK obsahuje asynchronní rozhraní API a používá neblokující vstupně-výstupní rozhraní API netty. Práce vstupně-výstupních operací sady SDK se provádí na vláknech IO Netty. Počet vláken IO Netty je nakonfigurovaný tak, aby byl stejný jako počet jader procesoru počítače aplikace.

Vlákna Netty vstupně-výstupních operací jsou určena pouze pro neblokující vstupně-výstupní operace Netty. Sada SDK vrátí výsledek vyvolání rozhraní API na jednom ze vstupně-výstupních vláken Netty do kódu aplikace. Pokud aplikace po přijetí výsledků na vlákně Netty provede dlouhou operaci, nemusí sada SDK mít dostatek vstupně-výstupních vláken k provedení interní vstupně-výstupních operací. Takové kódování aplikací může mít za následek nízkou propustnost, vysokou latenci a io.netty.handler.timeout.ReadTimeoutException selhání. Alternativním řešením je přepnout vlákno, když víte, že operace nějakou dobu trvá.

Podívejte se například na následující fragment kódu. Můžete provádět dlouhou práci, která trvá více než několik milisekund na vlákně Netty. Pokud ano, můžete se nakonec dostat do stavu, ve kterém není k dispozici žádné vlákno netty vstupně-výstupních operací pro zpracování vstupně-výstupních operací. Výsledkem je chyba ReadTimeoutException.

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);
}

Alternativním řešením je změnit vlákno, na kterém provádíte práci, která nějakou dobu trvá. Definujte jednu instanci plánovače pro vaši aplikaci.

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);

Možná budete muset udělat práci, která nějakou dobu trvá, například výpočetně náročné práce nebo blokování vstupně-výstupních operací. V tomto případě přepněte vlákno na pracovní proces poskytovaný vaším customScheduler rozhraním .observeOn(customScheduler) API.

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(
            // ...
        );

Pomocí příkazu observeOn(customScheduler)uvolníte vlákno Netty io a přepnete na vlastní vlákno poskytované vlastním plánovačem. Tato úprava řeší problém. Už nebudete mít io.netty.handler.timeout.ReadTimeoutException chybu.

Problém s vyčerpáním fondu Připojení

PoolExhaustedException je selhání na straně klienta. Tato chyba značí, že vaše úloha aplikace je vyšší než to, co může sloužit fond připojení sady SDK. Zvětšete velikost fondu připojení nebo distribuujte zatížení více aplikací.

Příliš vysoká frekvence požadavků

Toto selhání je selhání na straně serveru. Označuje, že jste spotřebovali zřízenou propustnost. Zkuste to znovu později. Pokud k tomuto selhání dochází často, zvažte zvýšení propustnosti kolekce.

Selhání při připojování k emulátoru služby Azure Cosmos DB

Certifikát HTTPS emulátoru služby Azure Cosmos DB je podepsaný svým držitelem. Aby sada SDK fungovala s emulátorem, naimportujte certifikát emulátoru do java TrustStore. Další informace najdete v tématu Export certifikátů emulátoru služby Azure Cosmos DB.

Problémy se konflikty závislostí

Exception in thread "main" java.lang.NoSuchMethodError: rx.Observable.toSingle()Lrx/Single;

Výše uvedená výjimka naznačuje, že máte závislost na starší verzi knihovny RxJava (např. 1.2.2). Naše sada SDK spoléhá na RxJava 1.3.8, která má rozhraní API, která nejsou k dispozici ve starší verzi RxJava.

Alternativním řešením takových problémů je určit, která jiná závislost přináší RxJava-1.2.2, a vyloučit tranzitivní závislost na RxJava-1.2.2 a povolit, aby sada CosmosDB SDK přinesla novější verzi.

Pokud chcete zjistit, která knihovna přináší RxJava-1.2.2, spusťte následující příkaz vedle souboru pom.xml projektu:

mvn dependency:tree

Další informace najdete v průvodci stromem závislostí maven.

Jakmile identifikujete RxJava-1.2.2, je tranzitivní závislost, ze které další závislosti vašeho projektu, můžete upravit závislost na této knihovně v souboru pom a vyloučit tranzitivní závislost RxJava:

<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>

Další informace najdete v průvodci vyloučením tranzitivní závislosti.

Povolení protokolování klientské sady SDK

Sada Java Async SDK používá jako fasádu protokolování SLF4j, která podporuje protokolování do oblíbených rozhraní protokolování, jako jsou log4j a logback.

Pokud například chcete jako rozhraní protokolování použít log4j, přidejte do cesty ke třídám Java následující knihovny.

<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>

Přidejte také konfiguraci log4j.

# 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

Další informace najdete v návodu k protokolování sfl4j.

Statistika sítě operačního systému

Spuštěním příkazu netstat získejte představu o tom, kolik připojení je ve stavech, například ESTABLISHED a CLOSE_WAIT.

V Linuxu můžete spustit následující příkaz.

netstat -nap

Vyfiltrujte výsledek jenom na připojení ke koncovému bodu služby Azure Cosmos DB.

Počet připojení ke koncovému bodu služby Azure Cosmos DB ve ESTABLISHED stavu nesmí být větší než nakonfigurovaná velikost fondu připojení.

Ve stavu může být mnoho připojení ke koncovému CLOSE_WAIT bodu služby Azure Cosmos DB. Může existovat více než 1 000. Číslo, které je vysoké, značí, že se připojení navazují a rychle odtrhávají. Tato situace může způsobit problémy. Další informace najdete v části Běžné problémy a alternativní řešení .