本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
Lettuce 用戶端組態 (Valkey 和 Redis OSS)
本節說明建議的 Java 和 Lettuce 組態選項,以及它們如何套用至 ElastiCache 叢集。
本節中的建議已使用 Lettuce 版本 6.2.2 進行測試。
Java DNS 快取 TTL
Java 虛擬機器 (JVM) 會快取 DNS 名稱查詢。當 JVM 將主機名稱解析為 IP 位址時,它會在指定的時間段內快取 IP 位址,稱為存留時間 (TTL)。
選擇 TTL 值是為了在延遲和對變化的回應能力之間進行權衡。DNS 解析程式使用較短的 TTL,可以更快地注意到叢集 DNS 中的更新。這可讓您的應用程式更快回應叢集所經歷的替換或其他工作流程。不過,如果 TTL 太低,則會增加查詢量,進而增加應用程式的延遲時間。雖然沒有正確的 TTL 值,但在設定 TTL 值時,值得考量可以等待變更生效的時間長度。
由於 ElastiCache 節點使用可能會變更的 DNS 名稱項目,因此我們建議您將 JVM 設定為 5 到 10 秒的低 TTL。這可確保當節點的 IP 地址變更時,您的應用程式將可透過重新查詢 DNS 項目來接收並使用資源的新 IP 地址。
在一些 Java 組態上,JVM 的預設 TTL 會如此設定,在重新啟動 JVM 之前,「絕不」重新整理 DNS 項目。
如需如何設定 JVM TTL 的詳細資訊,請參閱如何設定 JVM TTL。
Lettuce 版本
建議使用 Lettuce 6.2.2 或更新版本。
端點
當您使用已啟用叢集模式的叢集時,請將 redisUri
設為叢集組態端點。此 URI 的 DNS 查閱會傳回叢集中所有可用節點的清單,並在叢集初始化期間隨機解析到其中之一。如需拓撲重新整理如何運作的詳細資訊,請參閱本主題稍後的 dynamicRefreshResources。
SocketOption
啟用 KeepAlive
請務必根據應用程式需求和工作負載設定連線逾時
ClusterClientOption:已啟用叢集模式的用戶端選項
當連線中斷時,啟用 AutoReconnect
設置 CommandTimeout
設定 nodeFilter
例如,在容錯移轉完成且叢集啟動復原程序之後,clusterTopology 正在重新整理時,叢集匯流排節點對應的時間很短,在完全從拓撲中移除之前,向下節點會列為 FAIL 節點。在此期間,Lettuce 用戶端會將其視為運作狀態良好的節點,並持續與其連線。這會在重試耗盡後導致失敗。
例如:
final ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder() ... // other options .nodeFilter(it -> ! (it.is(RedisClusterNode.NodeFlag.FAIL) || it.is(RedisClusterNode.NodeFlag.EVENTUAL_FAIL) || it.is(RedisClusterNode.NodeFlag.HANDSHAKE) || it.is(RedisClusterNode.NodeFlag.NOADDR))) .validateClusterNodeMembership(false) .build(); redisClusterClient.setOptions(clusterClientOptions);
注意
節點篩選最適合用於 DynamicRefreshSources 設為 true 時。否則,如果拓撲檢視是從單一有問題的種子節點中取得,並將某些碎片的主節點視為故障,則它將篩除此主節點,這將導致槽不被涵蓋。擁有多個種子節點 (DynamicrefreshSources 為真時) 可降低發生此問題的可能性,因為在使用新升級的主節點進行容錯移轉之後,至少部分種子節點應具有更新的拓撲檢視。
ClusterTopologyRefreshOptions:用於控制啟用叢集模式的用戶端的叢集拓撲重新整理選項
注意
已停用叢集模式的叢集不支援叢集探索命令,也不相容於所有用戶端動態拓撲探索功能。
ElastiCache 的停用叢集模式與 Lettuce 的 MasterSlaveTopologyRefresh
不相容。相反地,對於停用的叢集模式,您可以設定 StaticMasterReplicaTopologyProvider
並提供叢集讀取和寫入端點。
如需連接至已停用叢集模式之叢集的詳細資訊,請參閱 尋找 Valkey 或 Redis OSS (停用叢集模式) 叢集的端點 (主控台)。
如果您想要使用 Lettuce 的動態拓撲探索功能,可以使用與現有叢集相同的碎片組態來建立已啟用叢集模式的叢集。不過,對於已啟用叢集模式的叢集,建議至少設定 3 個具有至少 1 個複本的碎片,以支援快速容錯移轉。
啟用 enablePeriodicRefresh
啟用此選項後,您可以將此工作新增至背景任務,以減少與重新整理叢集拓撲相關的延遲。雖然拓撲重新整理是在背景工作中執行,但對於具有許多節點的叢集而言,可能會有些慢。這是因為所有節點都在查詢其檢視以取得最新的叢集檢視。如果您執行的是大型叢集,則可能需要增加期間。
啟用 enableAllAdaptiveRefreshTriggers
啟用 closeStaleConnections
啟用 dynamicRefreshResources
使用動態重新整理查詢所有探索到的叢集拓撲節點,並嘗試選擇最準確的叢集檢視。如果其設定為 false,則只會使用初始種子節點做為拓撲探索的來源,而且只會取得初始種子節點的用戶端數量。在停用時,如果叢集配置端點解析為故障的節點,則嘗試重新整理叢集檢視會失敗,並導致例外狀況。這種情況可能會發生,因為從叢集組態端點移除故障節點的項目需要一些時間。因此,組態端點仍可在短時間內隨機解析為故障的節點。
但是,在其啟用後,我們會使用從叢集檢視接收到的所有叢集節點,來查詢其目前的檢視。因為我們會從該檢視中篩選出故障的節點,所以拓撲重新整理將會成功。不過,當動態重新整理來源為真時,Lettuce 會查詢所有節點以取得叢集檢視,然後比較結果。因此,對於具有大量節點的叢集來說,它可能很昂貴。建議您在多節點的叢集時關閉此功能。
final ClusterTopologyRefreshOptions topologyOptions = ClusterTopologyRefreshOptions.builder() .enableAllAdaptiveRefreshTriggers() .enablePeriodicRefresh() .dynamicRefreshSources(true) .build();
ClientResources
使用 DirContextDnsResolver
使用指數退避和完全抖動來設定 reconnectDelay
ClientResources clientResources = DefaultClientResources.builder() .dnsResolver(new DirContextDnsResolver()) .reconnectDelay( Delay.fullJitter( Duration.ofMillis(100), // minimum 100 millisecond delay Duration.ofSeconds(10), // maximum 10 second delay 100, TimeUnit.MILLISECONDS)) // 100 millisecond base .build();
逾時
使用低於指令逾時的連線逾時值。Lettuce 使用延遲連接建立。因此,如果連線逾時高於指令逾時,而 Lettuce 嘗試連線到健康狀態不良的節點,且永遠超過指令逾時,則您可能會在拓撲重新整理後發生一段時間的持續性失敗。
針對不同的指令使用動態指令逾時。我們建議您根據指令預期持續時間設定指令逾時。例如,對重複多個金鑰的指令 (例如 FLUSHDB、FLUSHALL、KEYS、SMEMBERS 或 Lua 指令碼) 使用較長的逾時時間。對於單一金鑰指令 (例如 SET、GET 和 HSET) 使用較短的逾時。
注意
下列範例中設定的逾時適用於執行 SET/GET 指令的測試,且金鑰和值長度最多 20 個位元組。在指令很複雜或金鑰和值較大時,處理時間可能會更長。您應該根據應用程式的使用案例設定逾時。
private static final Duration META_COMMAND_TIMEOUT = Duration.ofMillis(1000); private static final Duration DEFAULT_COMMAND_TIMEOUT = Duration.ofMillis(250); // Socket connect timeout should be lower than command timeout for Lettuce private static final Duration CONNECT_TIMEOUT = Duration.ofMillis(100); SocketOptions socketOptions = SocketOptions.builder() .connectTimeout(CONNECT_TIMEOUT) .build(); class DynamicClusterTimeout extends TimeoutSource { private static final Set<ProtocolKeyword> META_COMMAND_TYPES = ImmutableSet.<ProtocolKeyword>builder() .add(CommandType.FLUSHDB) .add(CommandType.FLUSHALL) .add(CommandType.CLUSTER) .add(CommandType.INFO) .add(CommandType.KEYS) .build(); private final Duration defaultCommandTimeout; private final Duration metaCommandTimeout; DynamicClusterTimeout(Duration defaultTimeout, Duration metaTimeout) { defaultCommandTimeout = defaultTimeout; metaCommandTimeout = metaTimeout; } @Override public long getTimeout(RedisCommand<?, ?, ?> command) { if (META_COMMAND_TYPES.contains(command.getType())) { return metaCommandTimeout.toMillis(); } return defaultCommandTimeout.toMillis(); } } // Use a dynamic timeout for commands, to avoid timeouts during // cluster management and slow operations. TimeoutOptions timeoutOptions = TimeoutOptions.builder() .timeoutSource( new DynamicClusterTimeout(DEFAULT_COMMAND_TIMEOUT, META_COMMAND_TIMEOUT)) .build();