Partilhar via


Solucionar problemas ao usar o Azure Cosmos DB Async Java SDK v2 com API para contas NoSQL

APLICA-SE A: NoSQL

Importante

Este não é o SDK Java mais recente para o Azure Cosmos DB! Você deve atualizar seu projeto para o Azure Cosmos DB Java SDK v4 e ler o guia de solução de problemas do Azure Cosmos DB Java SDK v4. Siga as instruções no guia Migrar para o Azure Cosmos DB Java SDK v4 e no guia Reator vs RxJava para atualizar.

Este artigo aborda a solução de problemas somente para o Azure Cosmos DB Async Java SDK v2. Consulte as Notas de versão do SDK Java v2 do Azure Cosmos DB Async, o repositório Maven e as dicas de desempenho para obter mais informações.

Importante

Em 31 de agosto de 2024, o Azure Cosmos DB Async Java SDK v2.x será desativado; o SDK e todos os aplicativos que usam o SDK continuarão a funcionar; O Azure Cosmos DB simplesmente deixará de fornecer mais manutenção e suporte para este SDK. Recomendamos seguir as instruções acima para migrar para o SDK Java v4 do Azure Cosmos DB.

Este artigo aborda problemas comuns, soluções alternativas, etapas de diagnóstico e ferramentas quando você usa o Java Async SDK com contas do Azure Cosmos DB para NoSQL. O Java Async SDK fornece representação lógica do lado do cliente para acessar o Azure Cosmos DB para NoSQL. Este artigo descreve as ferramentas e abordagens para o ajudar se encontrar problemas.

Comece com esta lista:

Problemas comuns e soluções

Problemas de rede, falha de tempo limite de leitura Netty, baixa taxa de transferência, alta latência

Sugestões gerais

  • Verifique se o aplicativo está sendo executado na mesma região que sua conta do Azure Cosmos DB.
  • Verifique o uso da CPU no host onde o aplicativo está sendo executado. Se o uso da CPU for de 90% ou mais, execute seu aplicativo em um host com uma configuração superior. Ou você pode distribuir a carga em mais máquinas.

Limitação de conexão

A limitação de conexão pode acontecer devido a um limite de conexão em uma máquina host ou ao esgotamento da porta do Azure SNAT (PAT).

Limite de conexão em uma máquina host

Alguns sistemas Linux, como o Red Hat, têm um limite superior no número total de arquivos abertos. Soquetes no Linux são implementados como arquivos, então esse número limita o número total de conexões também. Execute o seguinte comando.

ulimit -a

O número máximo de arquivos abertos permitidos, que são identificados como "nofile", precisa ser pelo menos o dobro do tamanho do pool de conexões. Para obter mais informações, consulte Dicas de desempenho.

Exaustão da porta do Azure SNAT (PAT)

Se a sua aplicação for implementada em Máquinas Virtuais do Azure sem um endereço IP público, por predefinição , as portas SNAT do Azure estabelecem ligações a qualquer ponto de extremidade fora da sua VM. O número de conexões permitidas da VM para o ponto de extremidade do Azure Cosmos DB é limitado pela configuração do Azure SNAT.

As portas SNAT do Azure são usadas somente quando sua VM tem um endereço IP privado e um processo da VM tenta se conectar a um endereço IP público. Há duas soluções alternativas para evitar a limitação do Azure SNAT:

  • Adicione seu ponto de extremidade de serviço do Azure Cosmos DB à sub-rede de sua rede virtual de Máquinas Virtuais do Azure. Para obter mais informações, consulte Pontos de extremidade de serviço da Rede Virtual do Azure.

    Quando o ponto de extremidade de serviço está habilitado, as solicitações não são mais enviadas de um IP público para o Azure Cosmos DB. Em vez disso, a rede virtual e a identidade da sub-rede são enviadas. Essa alteração pode resultar em quedas de firewall se apenas IPs públicos forem permitidos. Se você usar um firewall, ao habilitar o ponto de extremidade de serviço, adicione uma sub-rede ao firewall usando ACLs de Rede Virtual.

  • Atribua um IP público à sua VM do Azure.

Não é possível acessar o Serviço - firewall

ConnectTimeoutException indica que o SDK não pode acessar o serviço. Você pode obter uma falha semelhante à seguinte ao usar o modo direto:

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]

Se tiver uma firewall em execução no computador da aplicação, abra o intervalo de portas de 10 000 a 20 000 utilizado pelo modo direto. Siga também o Limite de conexão em uma máquina host.

Proxy HTTP

Se você usar um proxy HTTP, verifique se ele pode suportar o número de conexões configuradas no SDK ConnectionPolicy. Caso contrário, você enfrentará problemas de conexão.

Padrão de codificação inválido: bloqueando thread Netty IO

O SDK usa a biblioteca Netty IO para se comunicar com o Azure Cosmos DB. O SDK tem APIs assíncronas e usa APIs de E/S sem bloqueio do Netty. O trabalho de E/S do SDK é executado em threads do IO Netty. O número de threads do IO Netty é configurado para ser o mesmo que o número de núcleos de CPU da máquina do aplicativo.

Os threads Netty IO destinam-se a ser usados apenas para trabalho Netty IO sem bloqueio. O SDK retorna o resultado da invocação da API em um dos threads de E/S do Netty para o código do aplicativo. Se o aplicativo executar uma operação de longa duração depois de receber resultados no thread Netty, o SDK pode não ter threads de E/S suficientes para executar seu trabalho de E/S interno. Essa codificação de aplicativo pode resultar em baixa taxa de transferência, alta latência e io.netty.handler.timeout.ReadTimeoutException falhas. A solução alternativa é alternar o thread quando você sabe que a operação leva tempo.

Por exemplo, dê uma olhada no trecho de código a seguir. Você pode executar um trabalho de longa duração que leva mais do que alguns milissegundos no thread Netty. Se assim for, você eventualmente pode entrar em um estado em que nenhum thread Netty IO está presente para processar o trabalho de E/S. Como resultado, você obtém uma falha 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);
}

A solução alternativa é alterar o thread no qual você executa o trabalho que leva tempo. Defina uma instância singleton do agendador para seu aplicativo.

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

Você pode precisar fazer um trabalho que leva tempo, por exemplo, trabalho computacionalmente pesado ou bloqueio de E/S. Nesse caso, alterne o thread para um trabalhador fornecido pelo seu customScheduler usando a .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(
            // ...
        );

observeOn(customScheduler)Usando o , você libera o thread Netty IO e alterna para seu próprio thread personalizado fornecido pelo agendador personalizado. Esta modificação resolve o problema. Você não terá mais um io.netty.handler.timeout.ReadTimeoutException fracasso.

Problema esgotado do pool de conexões

PoolExhaustedException é uma falha do lado do cliente. Essa falha indica que a carga de trabalho do seu aplicativo é maior do que o pool de conexões SDK pode servir. Aumente o tamanho do pool de conexões ou distribua a carga em vários aplicativos.

A taxa de pedidos é demasiado grande

Esta falha é uma falha do lado do servidor. Ele indica que você consumiu sua taxa de transferência provisionada. Tente novamente mais tarde. Se você tiver essa falha com frequência, considere um aumento na taxa de transferência da coleta.

Falha na conexão com o emulador do Azure Cosmos DB

O certificado HTTPS do Emulador do Azure Cosmos DB é autoassinado. Para que o SDK funcione com o emulador, importe o certificado do emulador para um Java TrustStore. Para obter mais informações, consulte Exportar certificados do emulador do Azure Cosmos DB.

Problemas de conflito de dependência

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

A exceção acima sugere que você tem uma dependência de uma versão mais antiga do RxJava lib (por exemplo, 1.2.2). Nosso SDK depende do RxJava 1.3.8 que tem APIs não disponíveis na versão anterior do RxJava.

A solução alternativa para esses problemas é identificar qual outra dependência traz o RxJava-1.2.2 e excluir a dependência transitiva do RxJava-1.2.2 e permitir que o SDK do CosmosDB traga a versão mais recente.

Para identificar qual biblioteca traz o RxJava-1.2.2, execute o seguinte comando ao lado do arquivo pom.xml do projeto:

mvn dependency:tree

Para obter mais informações, consulte o guia da árvore de dependência maven.

Depois de identificar RxJava-1.2.2 é dependência transitiva de qual outra dependência do seu projeto, você pode modificar a dependência dessa lib em seu arquivo pom e excluir a dependência transitiva RxJava dela:

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

Para obter mais informações, consulte o guia de dependência transitiva de exclusão.

Habilitar o log do SDK do cliente

O Java Async SDK usa SLF4j como a fachada de log que suporta o login em estruturas de log populares, como log4j e logback.

Por exemplo, se você quiser usar log4j como a estrutura de log, adicione as seguintes libs em seu classpath Java.

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

Adicione também uma configuração 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

Para obter mais informações, consulte o manual de registro em log sfl4j.

Estatísticas da rede do SO

Execute o comando netstat para ter uma noção de quantas conexões estão em estados como ESTABLISHED e CLOSE_WAIT.

No Linux, você pode executar o seguinte comando.

netstat -nap

Filtre o resultado apenas para conexões com o ponto de extremidade do Azure Cosmos DB.

O número de conexões com o ponto de extremidade do Azure Cosmos DB no estado não pode ser maior do que o ESTABLISHED tamanho do pool de conexões configurado.

Muitas conexões com o ponto de extremidade do Azure Cosmos DB podem estar no CLOSE_WAIT estado. Pode haver mais de 1.000. Um número alto indica que as conexões são estabelecidas e derrubadas rapidamente. Esta situação pode causar problemas. Para obter mais informações, consulte a seção Problemas comuns e soluções alternativas .