針對搭配使用 Azure Cosmos DB Java SDK v4 和 API for NoSQL 帳戶時所發生的問題進行疑難排解
適用於:NoSQL
重要
本文只涵蓋適用於 Azure Cosmos DB Java SDK v4 的疑難排解。 如需詳細資訊,請參閱 Azure Cosmos DB Java SDK v4 版本資訊、Maven 存放庫,以及效能提示。 如果您目前使用的版本比 v4 還要舊,則請參閱移轉至 Azure Cosmos DB Java SDK v4 指南,以取得升級至 v4 的協助。
此文章涵蓋搭配使用 Azure Cosmos DB Java SDK v4 和 Azure Cosmos DB for NoSQL 帳戶時的常見問題、因應措施、診斷步驟與工具。 Azure Cosmos DB Java SDK v4 提供用戶端邏輯表示法來存取 Azure Cosmos DB for NoSQL。 此文章所說明的工具和方法,可以在您遇到任何問題時提供協助。
從此清單開始:
- 查看此文章的常見問題和因應措施一節。
- 查看 Azure Cosmos DB 中央存放庫中的 Java SDK,其可從 GitHub 上的開放原始碼取得。 它有受到主動監視的議題區段。 查看您是否有任何含有已提出因應措施的類似問題。 其中一個有用的秘訣是依
*cosmos:v4-item*
標記來篩選問題。 - 為 Azure Cosmos DB Java SDK v4 檢閱效能秘訣,並遵循建議的做法。
- 如果找不到解決方案,請閱讀本文的其餘部分。 然後提出 GitHub 問題。 如果有選項可將標籤新增至您的 GitHub 問題,則請新增
*cosmos:v4-item*
標記。
擷取診斷
JAVA V4 SDK 中的資料庫、容器、項目和查詢回應都有一個診斷屬性。 此屬性記錄與單一要求相關的所有資訊,包括是否有重試或任何暫時性失敗。
診斷以字串形式傳回。 每個版本會改進以針對不同情節更有效疑難排解,所以此字串也隨之變更。 使用每個版本的 SDK 時,字串可能會破壞並格式。 請勿剖析此字串,以免造成重大變更。
下列程式碼範例示範如何使用 Java V4 SDK 來讀取診斷記錄:
重要
建議您驗證 Java V4 SDK 的最低建議版本,並確定您使用的是此版本或更高版本。 您可以在這裡查看建議的版本。
資料庫作業
CosmosDatabaseResponse databaseResponse = client.createDatabaseIfNotExists(databaseName);
CosmosDiagnostics diagnostics = databaseResponse.getDiagnostics();
logger.info("Create database diagnostics : {}", diagnostics);
容器作業
CosmosContainerResponse containerResponse = database.createContainerIfNotExists(containerProperties,
throughputProperties);
CosmosDiagnostics diagnostics = containerResponse.getDiagnostics();
logger.info("Create container diagnostics : {}", diagnostics);
項目作業
// 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);
查詢作業
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());
});
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);
}
記錄診斷
Java V4 SDK v4.43.0 版和更新版本支援在所有要求或錯誤都符合特定準則時自動記錄 Cosmos 診斷。 應用程式開發人員可以定義延遲閾值 (針對點 (建立、讀取、取代、upsert、修補) 或非點作業 (查詢、變更摘要、大量和批次))、要求費用和承載大小。 如果要求超出這些定義的閾值,則將會自動發出這些要求的 cosmos 診斷。
根據預設,Java v4 SDK 會以特定格式自動記錄這些診斷。 不過,實作 CosmosDiagnosticsHandler
介面,並提供您自己的自訂診斷處理程式,可以變更此設定。
然後,這些 CosmosDiagnosticsThresholds
和 CosmosDiagnosticsHandler
可以用於 CosmosClientTelemetryConfig
物件中,而此物件應該在建立同步或非同步用戶端時傳遞至 CosmosClientBuilder
。
注意:這些診斷閾值會套用到不同類型的診斷,包括記錄、追蹤和用戶端遙測。
下列程式碼範例顯示如何定義診斷閾值和自訂診斷記錄器,並透過用戶端遙測設定加以使用:
定義自訂診斷閾值
// 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);
定義自訂診斷處理常式
// 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());
}
};
Defining 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();
重試設計
如需如何設計具復原性應用程式的指導,並瞭解 SDK 的重試語意,請參閱使用 Azure Cosmos DB SDK 設計具復原性的應用程式指南。
常見問題和因應措施
檢查入口網站計量
檢查入口網站計量有助於判斷是否為用戶端問題,或者服務是否有問題。 例如,如果計量包含高比率的速率限制要求 (HTTP 狀態碼 429),這表示要求正在進行節流處理,然後檢查要求率太大區段。
網路問題, Netty 讀取逾時失敗, 低輸送量, 高延遲
一般建議
若要獲得最佳效能︰
- 確定應用程式會在與您 Azure Cosmos DB 帳戶相同的區域上執行。
- 在應用程式執行所在的主機上檢查 CPU 使用量。 如果 CPU 使用量為 50% 或更高,請在具有更高組態的主機上執行您的應用程式。 或者,您可將負載分散於更多電腦上。
- 如果您要在 Azure Kubernetes Service 上執行應用程式,則可以使用 Azure 監視器來監視 CPU 使用率。
連線節流
連線節流的發生原因可能是因為主機電腦上的連線限制或 Azure SNAT (PAT) 連接埠耗盡。
主機電腦的連線限制
某些 Linux 系統 (例如 Red Hat) 具有開啟檔案的總數上限。 Linux 中的通訊端會實作為檔案,因此,這個數字也會限制連線總數。 執行下列命令。
ulimit -a
允許的開啟檔案數目上限 (識別為 "nofile") 必須至少是連線集區大小的兩倍。 如需詳細資訊,請參閱 Azure Cosmos DB Java SDK v4 效能秘訣。
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。 另請遵循主機電腦的連線限制。
UnknownHostException
UnknownHostException 表示 JAVA 架構無法解析受影響機器內用於 Azure Cosmos DB 端點的 DNS 輸入。 請確認機器可以解析 DNS 輸入,或如果您有任何自訂 DNS 解析軟體 (例如 VPN、Proxy 或自訂解決方案),請確定其中對 DNS 端點 (由錯誤指出無法加以解析) 的設定正確無誤。 如果該錯誤繼續出現,建議透過對錯誤中所述的端點執行 curl
命令,以驗證該機器的 DNS 解析。
HTTP Proxy
如果您使用 HTTP Proxy,請確定它可以支援在 SDK ConnectionPolicy
中設定的連線數目。
否則就會遇到連線問題。
程式碼編寫模式無效:封鎖 Netty IO 執行緒
SDK 會使用 Netty IO 程式庫來與 Azure Cosmos DB 通訊。 SDK 具有 Async 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 執行緒上執行需要超過幾毫秒的長時間持續性工作。 若是如此,您最終會進入以下狀態:沒有任何可處理 IO 工作的 Netty IO 執行緒。 因此,您會收到 ReadTimeoutException 失敗。
Java SDK V4 (Maven com.azure::azure-cosmos) 非同步 API
//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);
因應措施是變更要在其上執行費時工作的執行緒。 為您的應用程式定義排程器的單一執行個體。
Java SDK V4 (Maven com.azure::azure-cosmos) 非同步 API
// Have a singleton instance of an executor and a scheduler.
ExecutorService ex = Executors.newFixedThreadPool(30);
Scheduler customScheduler = Schedulers.fromExecutor(ex);
您可能需要執行費時的工作,比方說,耗用大量運算資源的工作或封鎖 IO。 在此情況下,使用 .publishOn(customScheduler)
API,將執行緒切換至 customScheduler
所提供的背景工作角色。
Java SDK V4 (Maven com.azure::azure-cosmos) 非同步 API
container.createItem(family)
.publishOn(customScheduler) // Switches the thread.
.subscribe(
// ...
);
使用 publishOn(customScheduler)
,您就能釋放 Netty IO 執行緒,並切換至您自己由 customScheduler 所提供的自訂執行緒。 這項修改可解決問題。 您不會再發生 io.netty.handler.timeout.ReadTimeoutException
失敗。
要求率太大
此失敗為伺服器端失敗。 其指出您已取用您佈建的輸送量。 請稍後再試。 如果您經常發生此失敗,請考慮增加集合輸送量。
依 getRetryAfterInMilliseconds 間隔實作輪詢
在進行效能測試期間,您應該增加負載,直到系統對小部分要求進行節流處理為止。 如果進行節流處理,用戶端應用程式應該降速,且持續時間達伺服器指定的重試間隔。 採用降速可確保您在重試之間花費最少的等待時間。
JAVA SDK 回應鏈錯誤處理
在用戶端的應用程式邏輯方面,透過 Azure Cosmos DB Java SDK 進行錯誤處理十分重要。 Reactor 核心架構提供可用於不同情節的不同錯誤處理機制。 建議客戶詳細了解這些錯誤處理運算子,使用最符合自身重試邏輯情境的選項。
重要
不建議使用 onErrorContinue()
運算子,因為這並非在所有情境下都能支援。
請注意,onErrorContinue()
是會讓回應鏈行為變得不明確的專家運算子。 此運算子會在上游運作 (而非下游運算子),且需要特定的運算子支援才能運作,其範圍可輕易地將上游傳播至無此預期的程式庫程式碼 (而導致非預期的行為)。 如需這個特殊運算子的詳細資訊,請參閱 onErrorContinue()
的文件。
無法連線至 Azure Cosmos DB 模擬器
Azure Cosmos DB 模擬器的 HTTPS 憑證是自我簽署的。 針對要與模擬器搭配運作的 SDK,將模擬器憑證匯入到 Java TrustStore。 如需詳細資訊,請參閱匯出 Azure Cosmos DB 模擬器憑證。
相依性衝突問題
Azure Cosmos DB Java SDK 會提取許多相依性;一般來說,如果您的專案相依性樹狀結構包括 Azure Cosmos DB Java SDK 相依的舊版成品,則這可能會導致您在執行應用程式時產生未預期的錯誤。 如果您要對應用程式非預期地擲出例外狀況的原因進行偵錯,則最好再次檢查您的相依性樹狀結構是否意外地提取舊版的一或多個 Azure Cosmos DB Java SDK 相依性。
這類問題的因應措施是找出哪些專案相依性會帶入舊版本,並排除舊版的可轉移相依性,並允許 Azure Cosmos DB Java SDK 帶入較新的版本。
若要識別哪些專案相依性會帶入 Azure Cosmos DB Java SDK 相依的舊版本,請針對您的專案 pom.xml 檔案執行下列命令:
mvn dependency:tree
如需詳細資訊,請參閱 maven 相依性樹狀結構指南。
一旦知道哪個專案相依性相依於舊版本,即可修改 POM 檔案中的相依性,並排除可轉移相依性,請遵循下列範例 (假設 reactor-core 是過時的相依性):
<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>
如需詳細資訊,請參閱排除可轉移相依性指南。
啟用用戶端 SDK 記錄
Azure Cosmos DB Java SDK v4 會使用 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 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
如需詳細資訊,請參閱 sfl4j 記錄手冊。
OS 網路統計資料
執行 netstat 命令,以了解有多少個連線處於 ESTABLISHED
和 CLOSE_WAIT
狀態。
在 Linux上,您可以執行下列命令。
netstat -nap
在 Windows 上,您可以使用不同的引數旗標來執行相同的命令:
netstat -abn
只篩選出與 Azure Cosmos DB 端點連線的結果。
處於 ESTABLISHED
狀態且與 Azure Cosmos DB 端點連線的數目不能大於您所設定的連線集區大小。
與 Azure Cosmos DB 端點的許多連線可能處於 CLOSE_WAIT
狀態。 可能有超過 1,000 個。 很高的數字表示連線已建立且快速地中斷。 這種情況可能會造成問題。 如需詳細資訊,請參閱常見問題和因應措施一節。
常見的查詢問題
查詢計量有助於判斷查詢耗費大部分時間的位置。 您可以從查詢計量查看在後端與用戶端花費多少時間。 深入了解查詢效能指南。