Partilhar via


Resolver problemas quando usa Azure Cosmos DB Java SDK v4 com API para contas NoSQL

Importante

Este artigo aborda apenas a resolução de problemas para o Azure Cosmos DB Java SDK v4. Consulte as notas de lançamento do Azure Cosmos DB Java SDK v4, o repositório Maven e dicas de desempenho para mais informações. Se você estiver usando uma versão mais antiga do que a v4, consulte o guia Migrar para o SDK Java do Azure Cosmos DB v4 para obter ajuda com a atualização para a v4.

Este artigo aborda problemas comuns, soluções alternativas, passos de diagnóstico e ferramentas quando utiliza o Azure Cosmos DB Java SDK v4 com o Azure Cosmos DB para contas NoSQL. O Azure Cosmos DB Java SDK v4 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:

  • Dê uma olhada na seção Problemas comuns e soluções alternativas neste artigo.
  • Veja o Java SDK no repositório central Azure Cosmos DB, que está disponível open source no GitHub. Tem uma secção de problemas que é monitorizada ativamente. Verifique se há algum problema semelhante com uma solução alternativa já reportado. Uma dica útil é filtrar os problemas pela *cosmos:v4-item* etiqueta.
  • Revise as dicas de desempenho para o Azure Cosmos DB Java SDK v4 e siga as práticas sugeridas.
  • Leia o resto deste artigo, caso não tenha encontrado uma solução. Depois apresenta uma edição no GitHub. Se houver uma opção para adicionar etiquetas ao teu problema no GitHub, adiciona uma *cosmos:v4-item* etiqueta.

Capturar os diagnósticos

As respostas de base de dados, contentores, itens e consultas no Java V4 SDK têm uma propriedade de Diagnóstico. Essa propriedade registra todas as informações relacionadas a uma única solicitação, incluindo se houve tentativas repetidas ou falhas transitórias.

Os Diagnósticos são devolvidos como uma cadeia. A cadeia muda a cada versão, à medida que é melhorada para resolver diferentes cenários. Com cada versão do SDK, a cadeia pode quebrar o seu formato. Não analise a cadeia de caracteres para evitar alterações disruptivas.

O seguinte exemplo de código mostra como ler registos de diagnóstico usando o Java V4 SDK:

Importante

Recomendamos validar a versão mínima recomendada do SDK Java V4 e garantir que está a usar esta versão ou superior. Pode consultar a versão recomendada aqui.

Operações com Base de Dados

CosmosDatabaseResponse databaseResponse = client.createDatabaseIfNotExists(databaseName);
CosmosDiagnostics diagnostics = databaseResponse.getDiagnostics();
logger.info("Create database diagnostics : {}", diagnostics); 

Operações de Contentores

CosmosContainerResponse containerResponse = database.createContainerIfNotExists(containerProperties,
                  throughputProperties);
CosmosDiagnostics diagnostics = containerResponse.getDiagnostics();
logger.info("Create container diagnostics : {}", diagnostics);

Operações da equipa

// Write Item
CosmosItemResponse<Family> item = container.createItem(family, new PartitionKey(family.getLastName()),
                    new CosmosItemRequestOptions());
        
CosmosDiagnostics diagnostics = item.getDiagnostics();
logger.info("Create item diagnostics : {}", diagnostics);
        
// Read Item
CosmosItemResponse<Family> familyCosmosItemResponse = container.readItem(documentId,
                    new PartitionKey(documentLastName), Family.class);
        
CosmosDiagnostics diagnostics = familyCosmosItemResponse.getDiagnostics();
logger.info("Read item diagnostics : {}", diagnostics);

Operações de Consulta

String sql = "SELECT * FROM c WHERE c.lastName = 'Witherspoon'";
        
CosmosPagedIterable<Family> filteredFamilies = container.queryItems(sql, new CosmosQueryRequestOptions(),
                    Family.class);
        
//  Add handler to capture diagnostics
filteredFamilies = filteredFamilies.handle(familyFeedResponse -> {
    logger.info("Query Item diagnostics through handle : {}", 
    familyFeedResponse.getCosmosDiagnostics());
});
        
//  Or capture diagnostics through iterableByPage() APIs.
filteredFamilies.iterableByPage().forEach(familyFeedResponse -> {
    logger.info("Query item diagnostics through iterableByPage : {}",
    familyFeedResponse.getCosmosDiagnostics());
});

Exceções do Azure Cosmos DB

try {
  CosmosItemResponse<Family> familyCosmosItemResponse = container.readItem(documentId,
                    new PartitionKey(documentLastName), Family.class);
} catch (CosmosException ex) {
  CosmosDiagnostics diagnostics = ex.getDiagnostics();
  logger.error("Read item failure diagnostics : {}", diagnostics);
}

Registar os diagnósticos

As versões do SDK Java V4 v4.43.0 e superiores suportam registo automático dos Diagnósticos do Cosmos para todos os pedidos ou erros, caso cumpram certos critérios. Os programadores de aplicações podem definir limiares para latência (para operações pontuais (criar, ler, substituir, atualizar, aplicar patch) ou não-pontuais (consulta, feed de alterações, bulk e batch)), carga de pedido e tamanho da carga. Se os pedidos excederem estes limiares definidos, os diagnósticos do cosmos para esses pedidos serão emitidos automaticamente.

Por defeito, o Java v4 SDK regista estes diagnósticos automaticamente num formato específico. No entanto, isto pode ser alterado ao implementar a interface CosmosDiagnosticsHandler e ao fornecer o seu próprio Manipulador de Diagnósticos personalizado.

Estes CosmosDiagnosticsThresholds e CosmosDiagnosticsHandler podem depois ser usados em CosmosClientTelemetryConfig objeto, que devem ser passados para CosmosClientBuilder no momento de criar o cliente de sincronização ou assíncrono.

NOTA: Estes limiares de diagnóstico são aplicados em diferentes tipos de diagnóstico, incluindo registo, rastreio e telemetria do cliente.

Os exemplos de código seguintes mostram como definir os limiares de diagnóstico, implementar um logger de diagnóstico personalizado e utilizá-los através da configuração de telemetria de cliente:

Definição de Limiares de Diagnóstico personalizados

//  Create diagnostics threshold
CosmosDiagnosticsThresholds cosmosDiagnosticsThresholds = new CosmosDiagnosticsThresholds();
//  These thresholds are for demo purposes
//  NOTE: Do not use the same thresholds for production
cosmosDiagnosticsThresholds.setPayloadSizeThreshold(100_00);
cosmosDiagnosticsThresholds.setPointOperationLatencyThreshold(Duration.ofSeconds(1));
cosmosDiagnosticsThresholds.setNonPointOperationLatencyThreshold(Duration.ofSeconds(5));
cosmosDiagnosticsThresholds.setRequestChargeThreshold(100f);

Definição de Manipulador de Diagnósticos personalizado

//  By default, DEFAULT_LOGGING_HANDLER can be used
CosmosDiagnosticsHandler cosmosDiagnosticsHandler = CosmosDiagnosticsHandler.DEFAULT_LOGGING_HANDLER;

//  App developers can also define their own diagnostics handler
cosmosDiagnosticsHandler = new CosmosDiagnosticsHandler() {
    @Override
    public void handleDiagnostics(CosmosDiagnosticsContext diagnosticsContext, Context traceContext) {
        logger.info("This is custom diagnostics handler: {}", diagnosticsContext.toJson());
    }
};

Definição do CosmosClientTelemetryConfig

//  Create Client Telemetry Config
CosmosClientTelemetryConfig cosmosClientTelemetryConfig =
    new CosmosClientTelemetryConfig();
cosmosClientTelemetryConfig.diagnosticsHandler(cosmosDiagnosticsHandler);
cosmosClientTelemetryConfig.diagnosticsThresholds(cosmosDiagnosticsThresholds);

//  Create sync client
CosmosClient client = new CosmosClientBuilder()
    .endpoint(AccountSettings.HOST)
    .key(AccountSettings.MASTER_KEY)
    .clientTelemetryConfig(cosmosClientTelemetryConfig)
    .buildClient();

Design de retentativas

Consulte nosso guia para projetar aplicativos resilientes com SDKs do Azure Cosmos DB para obter orientação sobre como projetar aplicativos resilientes e saiba quais são as semânticas de repetição do SDK.

Problemas comuns e soluções

Verifique as métricas do portal

Verificar as métricas do portal ajuda a determinar se é um problema do lado do cliente ou se há um problema com o serviço. Por exemplo, se as métricas contiverem uma alta taxa de solicitações com limitação de taxa (código de status HTTP 429), indicando que a solicitação está a ser restringida, consulte a seção Taxa de solicitação muito grande.

Problemas de rede, falha no tempo de espera de leitura do Netty, baixo débito, alta latência

Sugestões gerais

Para o melhor desempenho:

  • 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 da CPU for de 50 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 as dicas de desempenho do Azure Cosmos DB Java SDK v4.

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 a correr na sua máquina de aplicação, abra a gama de portas entre 10.000 e 20.000, que são usadas pelo modo direto. Siga também o limite de Ligação numa máquina anfitriã.

ExceçãoDeHostDesconhecido

UnknownHostException significa que o framework Java não consegue resolver a entrada DNS para o endpoint do Azure Cosmos DB na máquina afetada. Você deve verificar se a máquina pode resolver a entrada DNS ou, se você tiver algum software de resolução DNS personalizado (como VPN ou Proxy, ou uma solução personalizada), certifique-se de que ele contém a configuração correta para o ponto de extremidade DNS que o erro está alegando que não pode ser resolvido. Se o erro for constante, pode verificar a resolução DNS da máquina através do comando curl para o ponto de extremidade descrito no erro.

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 uma API Assíncrona 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 estão encarregados exclusivamente de realizar tarefas 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, que adiciona itens a um contentor ( veja aqui orientações sobre como configurar a base de dados e o contentor). Podes realizar trabalhos duradouros que demoram mais do que alguns milissegundos no tópico 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.

Java SDK V4 (Maven com.azure::azure-cosmos) API assíncrona


//Bad code with read timeout exception

int requestTimeoutInSeconds = 10;

/* ... */

AtomicInteger failureCount = new AtomicInteger();
// Max number of concurrent item inserts is # CPU cores + 1
Flux<Family> familyPub =
        Flux.just(Families.getAndersenFamilyItem(), Families.getAndersenFamilyItem(), Families.getJohnsonFamilyItem());
familyPub.flatMap(family -> {
    return container.createItem(family);
}).flatMap(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) {
    }
    return Mono.empty();
}).doOnError(Exception.class, exception -> {
    failureCount.incrementAndGet();
}).blockLast();
assert(failureCount.get() > 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.

Java SDK V4 (Maven com.azure::azure-cosmos) API assíncrona

// Have a singleton instance of an executor and a scheduler.
ExecutorService ex  = Executors.newFixedThreadPool(30);
Scheduler customScheduler = Schedulers.fromExecutor(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 .publishOn(customScheduler) API.

Java SDK V4 (Maven com.azure::azure-cosmos) API assíncrona

container.createItem(family)
        .publishOn(customScheduler) // Switches the thread.
        .subscribe(
                // ...
        );

Ao usar publishOn(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.

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.

  • Implementar o backoff em intervalos de getRetryAfterInMilliseconds

    Durante os testes de desempenho, deve-se aumentar a carga até que uma pequena taxa de pedidos seja controlada. Se for limitado, a aplicação cliente deve recuar para o intervalo de retentativa especificado pelo servidor. Respeitar o mecanismo de backoff garante que você passe o mínimo tempo possível a aguardar entre tentativas.

Tratamento de erros a partir do Java SDK Reactive Chain

O tratamento de erros a partir do Azure Cosmos DB Java SDK é importante quando se trata da lógica de aplicação do cliente. Existem diferentes mecanismos de gestão de erros fornecidos pelo framework reactor-core que podem ser usados em diferentes cenários. Recomendamos aos clientes que compreendam detalhadamente os operadores de gestão de erros e utilizem os que melhor se adequam aos seus cenários de lógica de retentativa.

Importante

Não recomendamos o uso onErrorContinue() do operador, pois não é suportado em todos os cenários. Note que onErrorContinue() é um operador especializado que pode tornar o comportamento da sua cadeia reativa pouco claro. Opera em operadores a montante, não a jusante, requer suporte específico do operador para funcionar, e o âmbito pode facilmente propagar-se a montante para o código da biblioteca que não o antecipou, resultando em comportamentos inesperados. Por favor, consulte a documentação de onErrorContinue() para mais detalhes sobre este operador especial.

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

O Azure Cosmos DB Java SDK incorpora muitas dependências; de um modo geral, se a árvore de dependência do seu projeto incluir uma versão mais antiga de um artefacto do qual o Azure Cosmos DB Java SDK depende, isso pode resultar em erros inesperados quando executa a sua aplicação. Se está a depurar a razão pela qual a sua aplicação lança uma exceção inesperadamente, é boa ideia verificar se a sua árvore de dependências não está a importar acidentalmente uma versão mais antiga de uma ou mais dependências do Azure Cosmos DB Java SDK.

A solução alternativa para tal problema é identificar qual das dependências do teu projeto traz a versão antiga e excluir a dependência transitiva dessa versão antiga, permitindo que o Azure Cosmos DB Java SDK traga a versão mais recente.

Para identificar qual das suas dependências de projeto traz uma versão mais antiga de algo de que o Azure Cosmos DB Java SDK depende, execute o seguinte comando no seu ficheiro de pom.xml projeto:

mvn dependency:tree

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

Depois de saber qual a dependência do seu projeto que depende de uma versão mais antiga, pode modificar a dependência dessa lib no seu ficheiro pom e excluir a dependência transitiva, seguindo o exemplo abaixo (que assume que o núcleo do reator é a dependência desatualizada):

<dependency>
  <groupId>${groupid-of-lib-which-brings-in-reactor}</groupId>
  <artifactId>${artifactId-of-lib-which-brings-in-reactor}</artifactId>
  <version>${version-of-lib-which-brings-in-reactor}</version>
  <exclusions>
    <exclusion>
      <groupId>io.projectreactor</groupId>
      <artifactId>reactor-core</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 Azure Cosmos DB Java SDK v4 utiliza o SLF4j como fachada de registo que suporta o registo em frameworks populares de registo, como o log4j e o 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 INFO and its only appender to A1.
log4j.rootLogger=INFO, A1

log4j.category.com.azure.cosmos=INFO
#log4j.category.io.netty=OFF
#log4j.category.io.projectreactor=OFF
# 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

No Windows, pode executar o mesmo comando com diferentes flags de argumento:

netstat -abn

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 .

Problemas comuns de consulta

As métricas de consulta ajudam a determinar onde a consulta está a passar a maior parte do tempo. A partir das métricas de consulta, você pode ver quanto está sendo gasto no back-end versus o cliente. Saiba mais no guia de desempenho da consulta.

Próximos passos

  • Saiba mais sobre as diretrizes de desempenho para o Java SDK v4
  • Aprenda sobre as melhores práticas para o Java SDK v4