Compartir a través de


Solución de problemas al usar el SDK de Java de Azure Cosmos DB v4 con api para cuentas NoSQL

Importante

En este artículo se describe la solución de problemas solo para el SDK de Java de Azure Cosmos DB v4. Consulte las notas de la versión v4 del SDK de Azure Cosmos DB para Java, el repositorio de Maven y las sugerencias de rendimiento para obtener más información. Si actualmente usa una versión anterior a la v4, consulte la guía Migración al SDK de Java de Azure Cosmos DB v4 para obtener ayuda con la actualización a la versión 4.

En este artículo se tratan problemas comunes, soluciones alternativas, pasos de diagnóstico y herramientas cuando se usa el SDK de Java de Azure Cosmos DB v4 con cuentas de Azure Cosmos DB para NoSQL. El SDK de Java de Azure Cosmos DB v4 proporciona una representación lógica del lado cliente para acceder a Azure Cosmos DB para NoSQL. En este artículo se describen herramientas y enfoques para ayudarle si surge algún problema.

Comience con esta lista:

  • Eche un vistazo a la sección Problemas y soluciones de este artículo.
  • Examine el SDK de Java en el repositorio central de Azure Cosmos DB, que está disponible de código abierto en GitHub. Tiene una sección de problemas que se supervisa activamente. Compruebe si encuentra algún problema similar con una solución alternativa ya registrada. Una sugerencia útil es filtrar los problemas por la *cosmos:v4-item* etiqueta .
  • Revise las sugerencias de rendimiento para el SDK de Java de Azure Cosmos DB v4 y siga los procedimientos sugeridos.
  • Lea el resto de este artículo si no encontró una solución. A continuación, abra un problema de GitHub. Si hay una opción para agregar etiquetas al problema de GitHub, agregue una *cosmos:v4-item* etiqueta.

Captura los diagnósticos

Las respuestas de diagnóstico para base de datos, contenedor, elemento y consulta en el SDK de Java V4 tienen una propiedad de diagnóstico. Esta propiedad registra toda la información relacionada con la solicitud única, incluidos los reintentos o errores transitorios.

Los datos de diagnóstico se devuelven como una cadena de texto. La cadena cambia con cada versión, ya que se mejora para solucionar problemas de diferentes escenarios. Con cada versión del SDK, la cadena podría alterar su formato. No analice la cadena para evitar cambios importantes.

En el ejemplo de código siguiente se muestra cómo leer los registros de diagnóstico mediante el SDK de Java V4:

Importante

Se recomienda validar la versión mínima recomendada del SDK de Java V4 y asegurarse de que usa esta versión o superior. Puede comprobar la versión recomendada aquí.

Operaciones de base de datos

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

Operaciones de contenedor

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

Operaciones de elemento

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

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

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

Registro de los diagnósticos

Las versiones del SDK de Java V4 v4.43.0 y versiones posteriores admiten el registro automático de Cosmos Diagnostics para todas las solicitudes o errores si cumplen determinados criterios. Los desarrolladores de aplicaciones pueden definir umbrales de latencia, para operaciones puntuales (crear, leer, reemplazar, upsert, patch) u operaciones no puntuales (consulta, alimentación de cambios, operaciones masivas y por lotes), carga de solicitudes y tamaño de los datos. Si las solicitudes superan estos umbrales definidos, los diagnósticos de Cosmos para esas solicitudes se emitirán automáticamente.

De forma predeterminada, el SDK de Java v4 registra estos diagnósticos automáticamente en un formato específico. Sin embargo, esto se puede cambiar implementando CosmosDiagnosticsHandler interfaz y proporcionando su propio manejador de diagnósticos personalizado.

CosmosDiagnosticsThresholds y CosmosDiagnosticsHandler pueden usarse en el objeto CosmosClientTelemetryConfig, que se debe pasar a CosmosClientBuilder al crear un cliente síncrono o asíncrono.

NOTA: Estos umbrales de diagnóstico se aplican en distintos tipos de diagnósticos, como el registro, el seguimiento y la telemetría del cliente.

Los ejemplos de código siguientes muestran cómo definir umbrales de diagnóstico, registrador de diagnóstico personalizado y usarlos a través de la configuración de telemetría de cliente:

Definición de umbrales 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);

Definición del controlador de diagnóstico 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());
    }
};

Definición de 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();

Diseño de reintento

Consulte nuestra guía para diseñar aplicaciones resistentes con los SDK de Azure Cosmos DB para instrucciones sobre cómo diseñar aplicaciones resistentes y saber cuál es la semántica de reintentos del SDK.

Problemas comunes y soluciones alternativas

Comprobación de las métricas del portal

La comprobación de las métricas del portal ayuda a determinar si se trata de un problema del lado cliente o si hay un problema con el servicio. Por ejemplo, si las métricas contienen una alta tasa de solicitudes de velocidad limitada (código de estado HTTP 429), lo que significa que la solicitud se ha limitado, consulte la sección Tasa de solicitudes demasiado grande.

Problemas de red, error de tiempo de espera de lectura de Netty, bajo rendimiento, latencia alta

Sugerencias generales

Para obtener el mejor rendimiento:

  • Asegúrese de que la aplicación se ejecuta en la misma región que la cuenta de Azure Cosmos DB.
  • Compruebe el uso de cpu en el host donde se ejecuta la aplicación. Si el uso de CPU es del 50 % o más, ejecute la aplicación en un host con una configuración superior. O bien, puede distribuir la carga en más máquinas.

Regulación de velocidad de conexión

La limitación de la conexión puede producirse debido a un límite de conexión en un equipo host o el agotamiento de puertos SNAT (PAT) de Azure.

Límite de conexión en un equipo host

Algunos sistemas Linux, como Red Hat, tienen un límite superior en el número total de archivos abiertos. Los sockets en Linux se implementan como archivos, por lo que este número limita también el número total de conexiones. Ejecute el siguiente comando.

ulimit -a

El número máximo permitido de archivos abiertos, que se identifican como "nofile", debe ser al menos el doble del tamaño del grupo de conexiones. Para más información, consulte las sugerencias de rendimiento del SDK de Java para Azure Cosmos DB v4.

Agotamiento de puertos SNAT (PAT) de Azure

Si la aplicación se implementa en Azure Virtual Machines sin una dirección IP pública, de forma predeterminada, los puertos SNAT de Azure establecen conexiones a cualquier punto de conexión fuera de la máquina virtual. El número de conexiones permitidas desde la máquina virtual hasta el punto de conexión de Azure Cosmos DB está limitado por la configuración de Azure SNAT.

Los puertos SNAT de Azure solo se usan cuando la máquina virtual tiene una dirección IP privada y un proceso de la máquina virtual intenta conectarse a una dirección IP pública. Hay dos soluciones alternativas para evitar la limitación de SNAT de Azure:

  • Agregue el punto de conexión de servicio de Azure Cosmos DB a la subred de la red virtual de Azure Virtual Machines. Para obtener más información, consulte puntos de conexión de servicio de red virtual de Azure.

    Cuando se habilita el punto de conexión de servicio, las solicitudes ya no se envían desde una dirección IP pública a Azure Cosmos DB. En su lugar, se envían la red virtual y la identidad de la subred. Este cambio puede producir caídas de firewall si solo se permiten direcciones IP públicas. Si usa un firewall, cuando se habilite el punto de conexión de servicio, agregue una subred al firewall mediante las ACL de Virtual Network.

  • Asigne una dirección IP pública a la máquina virtual de Azure.

No se puede acceder al servicio: firewall

ConnectTimeoutException indica que el SDK no puede acceder al servicio. Puede obtener un error similar al siguiente al usar el modo directo:

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]

Si tiene un firewall ejecutándose en la máquina de su aplicación, abra el rango de puertos de 10,000 a 20,000, que son utilizados por el modo directo. Siga también el límite de conexión en un equipo host.

UnknownHostException

UnknownHostException significa que el marco de Java no puede resolver la entrada DNS para el punto de conexión de Azure Cosmos DB en la máquina afectada. Debe comprobar que la máquina pueda resolver la entrada DNS o, si tiene algún software de resolución DNS personalizado (como VPN o Proxy, o una solución personalizada), asegúrese de que contiene la configuración correcta para el punto de conexión DNS que el error reclama que no se puede resolver. Si el error es constante, puede comprobar la resolución DNS de la máquina mediante un comando curl al punto de conexión descrito en el error.

Proxy HTTP

Si usa un Proxy HTTP, asegúrese de que pueda admitir el número de conexiones configuradas en el SDK de ConnectionPolicy. En caso contrario, se encontrará con problemas de conexión.

Patrón de codificación inválido: Bloqueo del hilo de E/S de Netty

El SDK usa la biblioteca de E/S de Netty para comunicarse con Azure Cosmos DB. El SDK tiene una API asincrónica y usa API de E/S sin bloqueo de Netty. El trabajo de E/S del SDK se realiza en subprocesos de E/S de Netty. El número de subprocesos de Netty I/O está configurado para que sea el mismo que el número de núcleos de CPU de la máquina de la aplicación.

Los subprocesos de E/S de Netty están diseñados para usarse solo para el trabajo de E/S de Netty sin bloqueo. El SDK devuelve el resultado de la invocación de API en uno de los subprocesos de E/S de Netty al código de la aplicación. Si la aplicación realiza una operación de larga duración después de recibir resultados en el subproceso de Netty, es posible que el SDK no tenga suficientes subprocesos de E/S para realizar su trabajo interno de E/S. Esta codificación de aplicaciones puede dar lugar a un rendimiento bajo, una latencia alta y io.netty.handler.timeout.ReadTimeoutException errores. La solución alternativa consiste en cambiar el subproceso cuando sabe que la operación tarda tiempo.

Por ejemplo, eche un vistazo al siguiente fragmento de código, que agrega elementos a un contenedor (busque aquí instrucciones sobre cómo configurar la base de datos y el contenedor). Es posible que realice un trabajo de larga duración que tarde más de unos milisegundos en el subproceso de Netty. Si es así, finalmente podría alcanzar un estado en el que no haya ningún subproceso de E/S de Netty presente para procesar el trabajo de E/S. Como resultado, obtendrá un error ReadTimeoutException.

API asincrónica del SDK para Java V4 (Maven com.azure::azure-cosmos)


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

La solución alternativa es cambiar el hilo en el que se realiza el trabajo que lleva tiempo. Defina una instancia singleton del planificador para su aplicación.

API asincrónica del SDK para Java V4 (Maven com.azure::azure-cosmos)

// Have a singleton instance of an executor and a scheduler.
ExecutorService ex  = Executors.newFixedThreadPool(30);
Scheduler customScheduler = Schedulers.fromExecutor(ex);

Es posible que tenga que realizar trabajos que requieren tiempo, por ejemplo, trabajos computacionalmente intensivos o de bloqueo de E/S. En este caso, cambie el hilo a un trabajador proporcionado por customScheduler mediante la API de .publishOn(customScheduler).

API asincrónica del SDK para Java V4 (Maven com.azure::azure-cosmos)

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

Mediante publishOn(customScheduler), libera el subproceso de E/S de Netty y cambia a su propio subproceso personalizado proporcionado por el planificador personalizado. Esta modificación resuelve el problema. Ya no obtendrás un io.netty.handler.timeout.ReadTimeoutException error.

Tasa de solicitudes demasiado grande

Este error es un error del lado servidor. Indica que ha consumido el rendimiento aprovisionado. Vuelva a intentarlo más tarde. Si recibe este error a menudo, considere un aumento en la capacidad de procesamiento de la colección.

  • Implementar el retroceso en los intervalos de getRetryAfterInMilliseconds

    Durante las pruebas de rendimiento, debe aumentar la carga hasta que se limite una pequeña tasa de solicitudes. Si se limita, la aplicación cliente debe retroceder para el intervalo de reintento especificado por el servidor. Respetar el retroceso garantiza que dedique un tiempo mínimo a la espera entre reintentos.

Control de errores de la cadena reactiva del SDK de Java

El control de errores del SDK de Java de Azure Cosmos DB es importante en lo que respecta a la lógica de la aplicación del cliente. Hay diferentes mecanismos de control de errores proporcionados por el marco de núcleo reactor que se puede usar en diferentes escenarios. Se recomienda a los clientes comprender estos operadores de control de errores con detalle y usar los que se ajustan a sus escenarios de lógica de reintento lo mejor.

Importante

No se recomienda usar onErrorContinue() el operador , ya que no se admite en todos los escenarios. Tenga en cuenta que onErrorContinue() es un operador especialista que puede hacer que el comportamiento de la cadena reactiva no esté claro. Funciona en operadores de datos ascendentes, no en descendentes. Requiere de soporte específico por parte de los operadores para funcionar, y el alcance puede propagarse fácilmente hacia arriba, afectando al código de la biblioteca que no lo previó, lo cual resulta en comportamiento inesperado. Consulte la documentación de onErrorContinue() para obtener más información sobre este operador especial.

Error al conectarse al emulador de Azure Cosmos DB

El certificado HTTPS del emulador de Azure Cosmos DB está autofirmado. Para que el SDK funcione con el emulador, importe el certificado del emulador en una instancia de Java TrustStore. Para más información, consulte Exportación de certificados del emulador de Azure Cosmos DB.

Problemas de conflicto de dependencias

El SDK de Java de Azure Cosmos DB extrae muchas dependencias; Por lo general, si el árbol de dependencias del proyecto incluye una versión anterior de un artefacto en el que depende el SDK de Java de Azure Cosmos DB, esto puede provocar errores inesperados que se generan al ejecutar la aplicación. Si está depurando por qué su aplicación arroja inesperadamente una excepción, es recomendable asegurarse de que el árbol de dependencias no esté incluyendo accidentalmente una versión anterior de alguna o varias dependencias del SDK de Java de Azure Cosmos DB.

La solución alternativa para este problema es identificar cuál de las dependencias del proyecto aporta la versión anterior y excluir la dependencia transitiva de esa versión anterior y permitir que el SDK de Java de Azure Cosmos DB incluya la versión más reciente.

Para identificar cuál de las dependencias del proyecto incluye una versión anterior de algo de lo que depende el SDK de Java de Azure Cosmos DB, ejecute el siguiente comando en el archivo pom.xml del proyecto:

mvn dependency:tree

Para obtener más información, consulte la guía del árbol de dependencias de Maven.

Una vez que sepa qué dependencia del proyecto depende de una versión anterior, puede modificar la dependencia de esa biblioteca en el archivo pom y excluir la dependencia transitiva, siguiendo el ejemplo siguiente (que supone que reactor-core es la dependencia obsoleta):

<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 obtener más información, consulte la guía de dependencias transitivas de exclusión.

Habilitación del registro del SDK de cliente

El SDK de Java de Azure Cosmos DB v4 usa slf4j como fachada de registro que admite el registro en frameworks de registro populares, como log4j y logback.

Por ejemplo, si desea usar log4j como marco de registro, agregue las siguientes bibliotecas en la ruta de clases de 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>

Agregue también una configuración 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 obtener más información, consulte el manual de registro de sfl4j.

Estadísticas de red del sistema operativo

Ejecute el comando netstat para obtener una idea del número de conexiones en estados como ESTABLISHED y CLOSE_WAIT.

En Linux, puede ejecutar el siguiente comando.

netstat -nap

En Windows, puede ejecutar el mismo comando con marcas de argumento diferentes:

netstat -abn

Filtre el resultado para mostrar solo las conexiones al punto de conexión de Azure Cosmos DB.

El número de conexiones al punto de conexión de Azure Cosmos DB en el estado ESTABLISHED no puede ser mayor que el tamaño configurado del grupo de conexiones.

Muchas conexiones al punto de conexión de Azure Cosmos DB pueden estar en estado CLOSE_WAIT . Puede haber más de 1000. Un número alto indica que las conexiones se establecen y se interrumpen rápidamente. Esta situación puede causar problemas. Para obtener más información, consulte la sección Problemas comunes y soluciones alternativas .

Problemas de consulta comunes

Las métricas de consulta ayudan a determinar dónde pasa la consulta la mayor parte del tiempo. En las métricas de consulta, puede ver la cantidad que se dedica al back-end en comparación con el cliente. Obtenga más información en la guía de rendimiento de consultas.

Pasos siguientes

  • Más información sobre las directrices de rendimiento para el SDK de Java v4
  • Más información sobre los procedimientos recomendados para el SDK de Java v4