你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
重要
这不是最新的 Azure Cosmos DB Java SDK! 应将项目升级到 Azure Cosmos DB Java SDK v4 ,然后阅读 Azure Cosmos DB Java SDK v4 故障排除指南。 按照 迁移到 Azure Cosmos DB Java SDK v4 指南和 Reactor vs RxJava 指南中的说明进行升级。
本文介绍仅适用于 Azure Cosmos DB Async Java SDK v2 的故障排除。 有关详细信息,请参阅 Azure Cosmos DB Async Java SDK v2 发行说明、Maven 存储库和性能提示。
重要
2024 年 8 月 31 日,Azure Cosmos DB Async Java SDK v2.x 将停用;使用 SDK 的 SDK 和所有应用程序 将继续正常运行;Azure Cosmos DB 将停止为此 SDK 提供进一步的维护和支持。 我们建议按照上述说明迁移到 Azure Cosmos DB Java SDK v4。
本文介绍将 Java Async SDK 与用于 NoSQL 帐户的 Azure Cosmos DB 配合使用时的常见问题、解决方法、诊断步骤和工具。 Java 异步 SDK 提供用于访问 Azure Cosmos DB for NoSQL 的客户端逻辑表示形式。 本文介绍了在遇到任何问题时可以提供帮助的工具和方法。
请从以下列表开始:
- 请查看本文中的常见问题和解决方法部分。
- 查看 GitHub 上可用的开放源代码的 SDK。 它有一个积极监控的问题部分。 检查是否已提交包含解决方法的任何类似问题。
- 查看性能提示并按照建议的做法进行操作。
- 如果找不到解决方案,请阅读本文的其余部分。 然后提交 GitHub 问题。
常见问题和解决方法
网络问题,Netty 读取超时失败,吞吐量低,延迟高
常规建议
- 确保应用在 Azure Cosmos DB 帐户所在的同一区域中运行。
- 检查运行应用的主机上的 CPU 使用情况。 如果 CPU 使用率为 90% 或更多,请在配置较高的主机上运行应用。 或者,可以在更多计算机上分配负载。
连接节流
由于主机计算机上的连接限制或Azure SNAT(PAT)端口耗尽,可能会发生连接限制。
主机上的连接限制
某些 Linux 系统(如 Red Hat)对打开的文件总数有上限。 Linux 中的套接字作为文件实现,因此此数字也会限制连接总数。 运行以下命令。
ulimit -a
允许的最大打开文件数(标识为“nofile”)需要至少是连接池大小的两倍。 有关详细信息,请参阅 性能提示。
Azure SNAT (PAT) 端口耗尽
如果应用部署在没有公共 IP 地址的 Azure 虚拟机上,则默认情况下 ,Azure SNAT 端口 与 VM 外部的任何终结点建立连接。 从 VM 到 Azure Cosmos DB 终结点,允许的连接数受 Azure SNAT 配置的限制。
仅当 VM 具有专用 IP 地址并且 VM 中的进程尝试连接到公共 IP 地址时,才使用 Azure SNAT 端口。 有两种解决方法可以避免 Azure SNAT 限制:
将您的 Azure Cosmos DB 服务终结点添加到 Azure 虚拟机虚拟网络的子网中。 有关详细信息,请参阅 Azure 虚拟网络服务终结点。
启用服务终结点后,不再从公共 IP 向 Azure Cosmos DB 发送请求, 而是发送虚拟网络和子网标识。 如果仅允许公共 IP,则此更改可能会导致防火墙丢失。 如果使用防火墙,则在启用服务终结点后,请使用虚拟网络 ACL 将子网添加到防火墙。
将公共 IP 分配给 Azure VM。
无法访问服务 - 防火墙
ConnectTimeoutException 指示 SDK 无法访问服务。
使用直接模式时,可能会遇到类似于以下内容的故障:
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]
如果应用计算机上运行防火墙,请打开直接模式使用的端口范围 10,000 到 20,000。 另请遵循 主机上的“连接”限制。
HTTP 代理
如果使用 HTTP 代理,请确保它支持 SDK ConnectionPolicy 中配置的连接数。
否则,将遇到连接问题。
无效的编码模式:阻止 Netty IO 线程
SDK 使用 Netty IO 库与 Azure Cosmos DB 通信。 SDK 具有异步 API,并使用 Netty 的非阻止 IO API。 SDK 的 IO 工作在 IO Netty 线程上执行。 IO Netty 线程数配置为与应用计算机的 CPU 核心数相同。
Netty IO 线程只用于执行非阻塞的 Netty IO 工作。 SDK 将其中一个 Netty IO 线程上的 API 调用结果返回到应用的代码。 如果应用在 Netty 线程上收到结果后执行持久作,则 SDK 可能没有足够的 IO 线程来执行其内部 IO 工作。 此类应用编码可能会导致低吞吐量、高延迟和 io.netty.handler.timeout.ReadTimeoutException 故障。 解决方法是在你知道操作需要时间时切换线程。
例如,请查看以下代码片段。 可以在 Netty 线程上执行耗时超过几毫秒的长期工作。 如果是这样,最终你可能会进入一种没有 Netty IO 线程来处理 IO 工作的状态。 因此,你会收到 ReadTimeoutException 失败。
异步 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);
}
解决方法是更改执行耗时工作的线程。 为应用定义调度器的单例实例。
异步 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);
可能需要执行需要时间的工作,例如计算密集型工作或阻塞 IO。 在这种情况下,请使用.observeOn(customScheduler) API 将线程切换为由你的customScheduler提供的工作线程。
异步 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),可以释放 Netty IO 线程并切换到自定义计划程序提供自己的自定义线程。
此修改解决了问题。 你不会再有 io.netty.handler.timeout.ReadTimeoutException 故障了。
连接池耗尽问题
PoolExhaustedException 是客户端故障。 此故障表明您的应用程序的工作负载超过了SDK连接池的服务能力。 增加连接池大小或在多个应用上分配负载。
请求速率过大
此故障是服务器端故障。 它指示已使用预配的吞吐量。 请稍后重试。 如果经常遇到此故障,请考虑增加集合吞吐量。
连接到 Azure Cosmos DB 模拟器失败
Azure Cosmos DB 模拟器 HTTPS 证书是自签名的。 若要使 SDK 使用模拟器,请将模拟器证书导入 Java TrustStore。 有关详细信息,请参阅 导出 Azure Cosmos DB 模拟器证书。
依赖项冲突问题
Exception in thread "main" java.lang.NoSuchMethodError: rx.Observable.toSingle()Lrx/Single;
上述异常表明你依赖于较旧版本的 RxJava lib(例如 1.2.2)。 我们的 SDK 依赖于 RxJava 1.3.8,它具有早期版本的 RxJava 中不可用的 API。
此类问题的解决方法是确定哪些其他依赖项引入 RxJava-1.2.2,并排除 RxJava-1.2.2 上的可传递依赖项,并允许 CosmosDB SDK 引入较新版本。
若要确定引入 RxJava-1.2.2 的库,请在项目 pom.xml 文件旁运行以下命令:
mvn dependency:tree
有关详细信息,请参阅 maven 依赖项树指南。
确定 RxJava-1.2.2 是项目中哪个其他依赖项的传递依赖之后,您可以在 pom 文件中修改该依赖库,并排除其对 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>
有关详细信息,请参阅 排除可传递依赖项指南。
启用客户端 SDK 日志记录
Java 异步 SDK 使用 SLF4j 作为日志记录外观,支持登录到常用的日志记录框架,例如 log4j 和 logback。
例如,如果要使用 log4j 作为日志记录框架,请在 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>
此外,添加 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
有关详细信息,请参阅 sfl4j 日志记录手册。
OS 网络统计信息
运行 netstat 命令,了解状态(如 ESTABLISHED 和 CLOSE_WAIT)中的连接数。
在 Linux 上,可以运行以下命令。
netstat -nap
将结果筛选为仅包括连接到 Azure Cosmos DB 终结点。
与 Azure Cosmos DB 终结点在 ESTABLISHED 状态的连接数不能大于您配置的连接池大小。
与 Azure Cosmos DB 终结点的许多连接可能处于 CLOSE_WAIT 状态。 可能超过 1,000 个。 如此高的数值表明连接已建立并快速拆除。 这种情况可能会导致问题。 有关详细信息,请参阅 “常见问题和解决方法 ”部分。