Partilhar via


Resolução de problemas quando usa o Azure Cosmos DB Async Java SDK v2 com API para contas NoSQL

Importante

Este não é o SDK Java mais recente para o Azure Cosmos DB! Deves atualizar o teu projeto para o Azure Cosmos DB Java SDK v4 e depois ler o guia de resolução de problemas do Azure Cosmos DB Java SDK v4. Siga as instruções no guia Migrate to Azure Cosmos DB Java SDK v4 e no guia Reactor vs RxJava para atualizar.

Este artigo aborda apenas a resolução de problemas do Azure Cosmos DB Async Java SDK v2. Consulte as notas de lançamento do Azure Cosmos DB Async Java SDK v2, o repositório Maven e dicas de desempenho para mais informações.

Importante

A 31 de agosto de 2024, o Azure Cosmos DB Async Java SDK v2.x será retirado; o SDK e todas as aplicações que utilizam o SDK continuarão a funcionar; O Azure Cosmos DB simplesmente deixará de fornecer manutenção e suporte adicionais para este SDK. Recomendamos seguir as instruções acima para migrar para o SDK Java v4 do Azure Cosmos DB.

Este artigo aborda questões comuns, soluções alternativas, passos de diagnóstico e ferramentas quando se utiliza o Java Async SDK com Azure Cosmos DB para contas NoSQL. O Java Async SDK fornece representação lógica do lado do cliente para aceder ao 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 no tempo de espera de leitura do Netty, baixo débito, alta latência

Sugestões gerais

  • Certifica-te de que a aplicação está a correr na mesma região da tua conta Azure Cosmos DB.
  • Verifica a utilização da CPU no host onde a aplicação está a correr. Se o uso do CPU for de 90 por cento ou mais, execute a sua aplicação num host com uma configuração superior. Ou pode distribuir a carga em mais máquinas.

Limitação da conexão

A limitação da ligação pode ocorrer devido a um limite de ligação numa máquina host ou ao esgotamento da porta Azure SNAT (PAT).

Limite de ligação numa máquina anfitriã

Alguns sistemas Linux, como o Red Hat, têm um limite superior para o número total de ficheiros abertos. Os sockets no Linux são implementados como ficheiros, por isso este número limita também o número total de ligações. Execute o seguinte comando.

ulimit -a

O número máximo de ficheiros abertos permitidos, identificados como "nofile", tem de ser pelo menos o dobro do tamanho do teu pool de ligações. Para mais informações, consulte Dicas de Performance.

Exaustão de portas Azure SNAT (PAT)

Se a sua aplicação estiver implementada em Máquinas Virtuais Azure sem um endereço IP público, por defeito as portas SNAT do Azure estabelecem ligações a qualquer endpoint 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 Azure SNAT são usadas apenas quando a sua VM tem um endereço IP privado e um processo da VM tenta ligar-se a um endereço IP público. Existem 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 finais de serviço da Azure Virtual Network.

    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 utilizar um firewall, ao ativar o ponto de extremidade do serviço, adicione uma sub-rede ao firewall usando ACLs de Rede Virtual.

  • Atribui um IP público à tua VM Azure.

Não consigo contactar o Serviço - firewall

ConnectTimeoutException indica que o SDK não consegue aceder ao serviço. Pode encontrar uma falha semelhante ao exemplo a seguir 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 um firewall em execução na sua máquina de aplicação, abra o intervalo de portas entre 10.000 e 20.000, que são utilizadas pelo modo direto. Siga também o limite de Ligação numa máquina anfitriã.

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: Bloqueio do thread Netty IO

O SDK utiliza a biblioteca Netty IO para comunicar com o Azure Cosmos DB. O SDK tem APIs Assíncronas e utiliza APIs de E/S não bloqueantes do Netty. O trabalho de IO do SDK é realizado em threads da IO Netty. O número de threads IO Netty está configurado para ser igual ao número de núcleos de CPU da máquina de aplicação.

Os threads de Netty IO destinam-se apenas a serem usados para trabalhos de Netty IO não bloqueantes. O SDK devolve o resultado da invocação da API numa das threads de IO do Netty ao código da aplicação. Se a aplicação realizar uma operação prolongada após receber resultados no thread Netty, o SDK pode não ter threads de IO suficientes para realizar o seu trabalho interno de IO. Este tipo de codificação de aplicações pode resultar em baixo rendimento, alta latência e io.netty.handler.timeout.ReadTimeoutException falhas. A solução alternativa é mudar a thread quando souberes que a operação demora tempo.

Por exemplo, veja o seguinte excerto de código. Poderá realizar trabalhos duradouros que demoram mais do que alguns milissegundos na thread Netty. Se sim, eventualmente podes chegar a um estado em que não existe nenhum thread de IO do Netty para processar as tarefas de IO. Como resultado, ocorre uma falha no 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 é mudar a thread em que fazes trabalhos que demoram tempo. Defina uma instância singleton do agendador para a sua aplicação.

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

Podes precisar de fazer trabalho que leva tempo, por exemplo, trabalho computacional pesado ou bloqueio de IO. Neste caso, muda o thread para um worker fornecido pelo teu 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(
            // ...
        );

Ao usar observeOn(customScheduler), liberta a thread Netty IO e muda para a sua própria thread personalizada fornecida pelo agendador personalizado. Esta modificação resolve o problema. Já não vais ter um io.netty.handler.timeout.ReadTimeoutException fracasso.

Problema de esgotamento do pool de conexões

PoolExhaustedException é uma falha do lado do cliente. Esta falha indica que a carga de trabalho da tua aplicação é superior à que o conjunto de ligação do SDK consegue servir. Aumenta o tamanho do pool de ligação ou distribui a carga em várias aplicações.

A taxa de pedidos é muito elevada

Esta falha é uma falha do lado do servidor. Indica que consumiu a sua capacidade de processamento provisionada. Tente novamente mais tarde. Se tiver esta falha frequentemente, considere aumentar a largura de banda da recolha.

Falha na ligação ao emulador de base de dados Azure Cosmos

O certificado HTTPS do emulador de base de dados do Azure Cosmos é auto-assinado. Para o SDK funcionar com o emulador, importa o certificado do emulador para uma Java TrustStore. Para mais informações, consulte Exportar certificados do emulador de base de dados Azure Cosmos.

Questões de Conflito de Dependência

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

A exceção acima sugere que tens uma dependência de uma versão mais antiga da lib RxJava (por exemplo, 1.2.2). O nosso SDK baseia-se no RxJava 1.3.8, que tem APIs não disponíveis nas versões anteriores do RxJava.

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

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

mvn dependency:tree

Para mais informações, consulte o guia da árvore de dependências do Maven.

Uma vez que identificar o RxJava-1.2.2 como dependência transitiva de qual outra dependência do seu projeto, pode modificar a dependência dessa lib no seu ficheiro pom e excluir a dependência transitiva do 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>

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

Ativar o registo do SDK do cliente

O Java Async SDK utiliza o SLF4j como fachada de registo que suporta o login em frameworks de registo populares como log4j e logback.

Por exemplo, se quiseres usar o log4j como framework de loging, adiciona as seguintes libs no teu caminho de classe 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>

Também adicione 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 mais informações, consulte o manual de registo sfl4j.

Estatísticas da rede OS

Executa o comando netstat para perceberes quantas ligações estão em estados como ESTABLISHED e CLOSE_WAIT.

No Linux, pode executar o seguinte comando.

netstat -nap

Filtre o resultado para apenas ligações ao endpoint do Azure Cosmos DB.

O número de ligações ao endpoint do Azure Cosmos DB nesse ESTABLISHED estado não pode ser superior ao tamanho do pool de ligação configurado.

Muitas ligações ao endpoint do Azure Cosmos DB podem estar nesse CLOSE_WAIT estado. Pode haver mais de 1.000. Um número tão alto indica que as ligações são estabelecidas e destruídas rapidamente. Esta situação pode causar problemas. Para mais informações, consulte a secção Problemas comuns e soluções alternativas .