You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Cosmos DB SDK 4.52.0读取数据时频繁出现状态码0的CosmosException

Java Cosmos DB SDK 频繁CosmosException排查与解决

问题背景

使用Java版Cosmos DB SDK 4.52.0同步读取容器数据时,频繁触发CosmosException,异常细节不足以定位根源。Cosmos DB主账号部署在美东(读写),日东部署只读副本;应用通过Spring Boot activeProfile在日东环境将日东设为首选区域。

配置信息

cosmosdb.endpoint = https://redacted.documents.azure.com:443/
cosmosdb.database = database1
cosmosdb.container = container1
cosmosdb.max-retry-wait-time = PT1S
cosmosdb.max-retry-attempts-on-throttled-requests = 0
cosmosdb.content-response-on-write-enabled = false
cosmosdb.cosmos-item-request-options.cosmos-end-to-end-operation-latency-policy-config-duration = PT0.020S

cosmosdb.connection-timeout = PT5S
cosmosdb.idle-connection-timeout = PT5M
cosmosdb.max-connections-per-endpoint = 100
cosmosdb.max-requests-per-connection = 30

核心代码片段

配置类代码

import com.azure.cosmos.CosmosEndToEndOperationLatencyPolicyConfig;
import com.azure.cosmos.CosmosEndToEndOperationLatencyPolicyConfigBuilder;
import com.azure.cosmos.GatewayConnectionConfig;
import com.azure.cosmos.ThrottlingRetryOptions;
import com.azure.cosmos.ConsistencyLevel;
import com.azure.cosmos.CosmosClient;
import com.azure.cosmos.CosmosClientBuilder;
import com.azure.cosmos.CosmosContainer;
import com.azure.cosmos.models.CosmosItemRequestOptions;
import com.azure.cosmos.models.CosmosPatchItemRequestOptions;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;
import java.util.List;

@Slf4j
@Configuration
public class CosmosClientConfigurer {
    @Bean
    GatewayConnectionConfig
    getGatewayConnectionConfig(@Value("${cosmosdb.idle-connection-timeout}") final Duration idleConnectionTimeout,
                               @Value("${cosmosdb.max-connections-per-endpoint}") final int maxConnectionsPerEndpoint) {

        final var gatewayConnectionConfig = new GatewayConnectionConfig()
                .setIdleConnectionTimeout(idleConnectionTimeout)
                .setMaxConnectionPoolSize(maxConnectionsPerEndpoint);

        log.info("GatewayConnectionConfig: {}", gatewayConnectionConfig);
        return gatewayConnectionConfig;
    }

    @Bean
    ThrottlingRetryOptions getThrottlingRetryOptions(
            @Value("${cosmosdb.max-retry-wait-time}") final Duration maxRetryWaitTime,
            @Value("${cosmosdb.max-retry-attempts-on-throttled-requests}") final int maxRetryAttemptsOnThrottledRequests) {

        final var throttlingRetryOptions = new ThrottlingRetryOptions()
                .setMaxRetryWaitTime(maxRetryWaitTime)
                .setMaxRetryAttemptsOnThrottledRequests(maxRetryAttemptsOnThrottledRequests);

        log.info("ThrottlingRetryOptions: {}", throttlingRetryOptions);
        return throttlingRetryOptions;
    }

    @Bean
    CosmosItemRequestOptions getCosmosItemRequestOptions(
            @Value("${cosmosdb.cosmos-item-request-options.cosmos-end-to-end-operation-latency-policy-config-duration}") final Duration duration) {

        final CosmosEndToEndOperationLatencyPolicyConfig latencyPolicyConfig =
                new CosmosEndToEndOperationLatencyPolicyConfigBuilder(duration).build();
        final CosmosItemRequestOptions cosmosItemRequestOptions = new CosmosPatchItemRequestOptions();
        cosmosItemRequestOptions.setCosmosEndToEndOperationLatencyPolicyConfig(latencyPolicyConfig);
        return cosmosItemRequestOptions;
    }

    CosmosContainer getUserProfileContainer(final CosmosClient cosmosClient,
                                            @Value("${cosmosdb.database}") final String database,
                                            @Value("${cosmosdb.container}") final String container) {

        return getCosmosContainer(cosmosClient, database, container);
    }


    private CosmosContainer getCosmosContainer(final CosmosClient cosmosClient,
                                               final String database,
                                               final String container) {
        return cosmosClient
                .getDatabase(database)
                .getContainer(container);
    }
    
    @Bean
    CosmosClient getCosmosClient(@Value("${cosmosdb.endpoint}") final String endpoint,
                                 @Value("${cosmosdb.content-response-on-write-enabled}") final boolean contentResponseOnWriteEnabled,
                                 @Value("${cosmosdb.key}") final String key,
                                 @Value("${spring.profiles.active}") final String activeProfile,
                                 final GatewayConnectionConfig gatewayConnectionConfig,
                                 final ThrottlingRetryOptions throttlingRetryOptions) {
    
            final List<String> japanEastRegion = List.of("Japan East");
            final CosmosClientBuilder cosmosClientBuilder = new CosmosClientBuilder()
                .endpoint(endpoint)
                .key(key)
                .throttlingRetryOptions(throttlingRetryOptions)
                .gatewayMode(gatewayConnectionConfig)
                .contentResponseOnWriteEnabled(contentResponseOnWriteEnabled)
                .consistencyLevel(ConsistencyLevel.EVENTUAL);

            if (activeProfile.equals("jpe"))
                cosmosClientBuilder.preferredRegions(japanEastRegion);

            return cosmosClientBuilder.buildClient();
    }
}

调用代码

public Optional<CosmosData> read(final String id) {

    try {
        final var startTime = Instant.now();

        final CosmosItemResponse<CosmosData> cosmosItemResponse = readFromCosmos(id);

        cosmosMetrics.recordClientSideReadLatency(Duration.between(startTime, Instant.now()));
        cosmosMetrics.recordServerSideReadLatency(cosmosItemResponse.getDuration());
        cosmosMetrics.recordReadRus(cosmosItemResponse.getRequestCharge());
        cosmosMetrics.countContainerStatusCodes(cosmosItemResponse.getStatusCode());

        return Optional.of(cosmosItemResponse.getItem());
    } catch (CosmosException e) {
        cosmosMetrics.countContainerStatusCodes(e.getStatusCode());
    } catch (Exception e) {
        cosmosMetrics.incrementErrorsForUserProfile(e);
    }
    return Optional.empty();
}
    

public CosmosItemResponse<CosmosData> readFromCosmos(final String id) throws CosmosException {

    final PartitionKey partitionKey = new PartitionKey(id);

    try {
        return
                cosmosContainer.readItem(
                        id,
                        partitionKey,
                        cosmosItemRequestOptions,
                        CosmosData.class
                );
    } catch (CosmosException e) {

        if (e.getStatusCode() == 404
                || e.getStatusCode() == 408
                || e.getStatusCode() == 409
                || e.getStatusCode() == 410
                || e.getStatusCode() == 429
                || e.getStatusCode() == 503) {

            log.info("Exception while reading for id: {}: {}", id, e.getMessage());
        } else {
            log.error("CosmosException for id {}: {}", id, e.getMessage());
        }
        throw e;
    }
}

捕获到的CosmosException片段

{
  "innerErrorMessage": null,
  "cosmosDiagnostics": {
    "userAgent": "azsdk-java-cosmos/4.52.0 Linux/5.15.0-1051-azure JRE/21.0.1",
    "activityId": "4f02cde3-c9c9-4c09-af6b-258f4d82db5d",
    "requestLatencyInMs": 0,
    "requestStartTimeUTC": "2023-12-15T18:23:24.381516148Z",
    "requestEndTimeUTC": "2023-12-15T18:23:24.381762054Z",
    "responseStatisticsList": [],
    "supplementalResponseStatisticsList": [],
    "addressResolutionStatistics": {},
    "regionsContacted": [
      "japan east"
    ],
    "retryContext": {
      "statusAndSubStatusCodes": null,
      "retryLatency": 0,
      "retryCount": 0
    },
    "metadataDiagnosticsContext": {
      "metadataDiagnosticList": null
    },
    "serializationDiagnosticsContext": {
      "serializationDiagnosticsList": null
    },
    "gatewayStatisticsList": [
      {
        "sessionToken": null,
        "operationType": "Read",
        "resourceType": "Document",
        "statusCode": 0,
        "subStatusCode": 0,
        "requestCharge": 0,
        "requestTimeline": [
          {
            "eventName": "connectionAcquired",
            "startTimeUTC": "2023-12-15T18:23:24.381635051Z",
            "durationInMilliSecs": 0.122203
          },
          {
            "eventName": "connectionConfigured",
            "startTimeUTC": null,
            "durationInMilliSecs": 0
          },
          {
            "eventName": "requestSent",
            "startTimeUTC": null,
            "durationInMilliSecs": 0
          },
          {
            "eventName": "transitTime",
            "startTimeUTC": null,
            "durationInMilliSecs": 0
          },
          {
            "eventName": "received",
            "startTimeUTC": null,
            "durationInMilliSecs": 0
          }
        ],
        "partitionKeyRangeId": null,
        "responsePayloadSizeInBytes": 0,
        "exceptionResponseHeaders": "{}"
      }
    ],
    "samplingRateSnapshot": 1,
    "systemInformation": {
      "usedMemory": "20807154 KB",
      "availableMemory": "10650126 KB",
      "systemCpuLoad": "(2023-12-15T18:22:55.851238339Z 40.2%), (2023-12-15T18:23:00.851247379Z 37.0%), (2023-12-15T18:23:05.851238334Z 35.5%), (2023-12-15T18:23:10.851252759Z 38.7%), (2023-12-15T18:23:15.851245468Z 42.3%), (2023-12-15T18:23:20.851240443Z 45.6%)",
      "availableProcessors": 31
    },
    "clientCfgs": {
      "id": 2,
      "machineId": "vmId_17b8a761-9046-429e-8c32-42505a66b26c",
      "connectionMode": "GATEWAY",
      "numberOfClients": 2,
      "excrgns": "[]",
      "clientEndpoints": {
        "https://redacted.documents.azure.com:443/": 2
      },
      "connCfg": {
        "rntbd": null,
        "gw": "(cps:10, nrto:PT1M, icto:PT5M, p:false)",
        "other": "(ed: true, cs: false, rv: true)"
      },
      "consistencyCfg": "(consistency: Eventual, mm: true, prgns: [japaneast])",
      "proactiveInit": "",
      "e2ePolicyCfg": ""
    }
  }
}

异常观察点

  • 请求时间线仅connectionAcquired事件有记录,requestSent等后续事件均为null,请求未真正发起
  • requestStartTimeUTC与requestEndTimeUTC间隔极短,请求未发送至Cosmos DB

排查结论与解决办法

核心原因

  1. End-to-End延迟策略阈值过短:配置的20毫秒阈值远低于跨区域请求的正常延迟,SDK提前终止请求
  2. 请求选项类型不匹配:读操作使用了用于更新的CosmosPatchItemRequestOptions,引发内部逻辑异常
  3. 网关连接缺失请求超时配置:未设置请求超时,连接获取后无后续处理触发异常
  4. 重试策略关闭:限流重试次数设为0,无容错能力应对跨区域网络波动

解决步骤

  1. 调整延迟策略阈值:将cosmos-end-to-end-operation-latency-policy-config-duration改为PT0.5S(500毫秒),匹配跨区域请求正常延迟
  2. 替换正确的请求选项类型:修改配置类中的请求选项Bean:
@Bean
CosmosItemRequestOptions getCosmosItemRequestOptions(
        @Value("${cosmosdb.cosmos-item-request-options.cosmos-end-to-end-operation-latency-policy-config-duration}") final Duration duration) {

    final CosmosEndToEndOperationLatencyPolicyConfig latencyPolicyConfig =
            new CosmosEndToEndOperationLatencyPolicyConfigBuilder(duration).build();
    final CosmosItemRequestOptions cosmosItemRequestOptions = new CosmosItemRequestOptions();
    cosmosItemRequestOptions.setCosmosEndToEndOperationLatencyPolicyConfig(latencyPolicyConfig);
    return cosmosItemRequestOptions;
}
  1. 补充网关请求超时配置:在GatewayConnectionConfig中添加请求超时设置:
@Bean
GatewayConnectionConfig
getGatewayConnectionConfig(@Value("${cosmosdb.idle-connection-timeout}") final Duration idleConnectionTimeout,
                           @Value("${cosmosdb.max-connections-per-endpoint}") final int maxConnectionsPerEndpoint,
                           @Value("${cosmosdb.connection-timeout}") final Duration connectionTimeout) {

    final var gatewayConnectionConfig = new GatewayConnectionConfig()
            .setIdleConnectionTimeout(idleConnectionTimeout)
            .setMaxConnectionPoolSize(maxConnectionsPerEndpoint)
            .setRequestTimeout(connectionTimeout);

    log.info("GatewayConnectionConfig: {}", gatewayConnectionConfig);
    return gatewayConnectionConfig;
}
  1. 开启限流重试:将max-retry-attempts-on-throttled-requests改为3-5,提升容错性:
cosmosdb.max-retry-attempts-on-throttled-requests = 3
  1. 优化异常日志:捕获CosmosException时打印完整诊断信息,便于排查:
log.error("CosmosException for id {}: {}", id, e.getDiagnostics());

内容的提问来源于stack exchange,提问作者Shubham

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 02:29:50