Lettuce 用戶端組態 (Valkey 和 RedisOSS) - Amazon ElastiCache

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

Lettuce 用戶端組態 (Valkey 和 RedisOSS)

本節說明建議的 Java 和 Lettuce 組態選項,以及這些選項如何套用至 ElastiCache 叢集。

本節中的建議已使用 Lettuce 版本 6.2.2 進行測試。

Java DNS快取 TTL

Java 虛擬機器 (JVM) 會快取DNS名稱查詢。當 將主機名稱JVM解析為 IP 地址時,它會快取 IP 地址一段時間,稱為 time-to-live(TTL)。

TTL 值的選擇是在延遲和對變更的回應能力之間取得權衡。透過較短的 TTLs,DNS解決者會DNS更快地通知叢集中的更新。這可讓您的應用程式更快回應叢集所經歷的替換或其他工作流程。不過,如果 TTL 太低,則會增加查詢磁碟區,進而增加應用程式的延遲。雖然沒有正確的TTL值,但建議您考慮在設定TTL值時,等待變更生效的時間長度。

由於 ElastiCache 節點使用可能會變更DNS的名稱項目,建議您JVM將 設定為 5 到 10 秒TTL的低點。這可確保當節點的 IP 地址變更時,您的應用程式將能夠透過重新查詢DNS項目來接收和使用資源的新 IP 地址。

在某些 Java 組態上,TTL會設定JVM預設值,因此在JVM重新啟動 之前,永遠不會重新整理DNS項目。

如需如何設定 JVM 的詳細資訊TTL,請參閱如何設定 JVM TTL

Lettuce 版本

建議使用 Lettuce 6.2.2 或更新版本。

端點

當您使用已啟用叢集模式的叢集時,請將 redisUri 設為叢集組態端點。此DNS查詢會URI傳回叢集中所有可用節點的清單,並在叢集初始化期間隨機解析為其中一個節點。如需拓撲重新整理運作方式的詳細資訊,請參閱本主題dynamicRefreshResources稍後的 。

SocketOption

啟用 KeepAlive。啟用此選項可減少在命令執行期間處理失敗連線的需求。

請務必根據應用程式需求和工作負載設定連線逾時。如需詳細資訊,請參閱本主題稍後的 Timeouts (逾時) 章節。

ClusterClientOption:啟用叢集模式的用戶端選項

連線中斷AutoReconnect時啟用 。

設定 CommandTimeout。如需更多詳細資料,請參閱本主題稍後的 Timeouts (逾時) 章節。

nodeFilter 設定為從拓撲篩選失敗的節點。Lettuce 會儲存用戶端「分割區」 (也稱為碎片」) 中「叢集節點」輸出中發現的所有節點 (包括具有 PFAIL/FAIL 狀態的節點)。在建立叢集拓撲的過程中,它會嘗試連線到所有的分割區節點。當節點因任何原因被取代時,新增故障節點的這種 Lettuce 行為可能會導致連線錯誤 (或警告)。

例如,在容錯移轉完成且叢集啟動復原程序後,當 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 為 true) 可降低此問題的可能性,因為至少一些種子節點在新升級的主要節點容錯移轉之後,應該具有更新的拓撲檢視。

ClusterTopologyRefreshOptions:用於控制叢集拓撲的選項,以重新整理已啟用叢集模式的用戶端

注意

已停用叢集模式的叢集不支援叢集探索命令,也不相容於所有用戶端動態拓撲探索功能。

與 停用的叢集模式與 Lettuce 的 ElastiCache 不相容MasterSlaveTopologyRefresh。相反地,對於停用的叢集模式,您可以設定 StaticMasterReplicaTopologyProvider 並提供叢集讀取和寫入端點。

如需連接至已停用叢集模式之叢集的詳細資訊,請參閱 尋找 Valkey 或 Redis OSS(停用叢集模式) 叢集的端點 (主控台)

如果您想要使用 Lettuce 的動態拓撲探索功能,可以使用與現有叢集相同的碎片組態來建立已啟用叢集模式的叢集。不過,對於已啟用叢集模式的叢集,建議至少設定 3 個具有至少 1 個複本的碎片,以支援快速容錯移轉。

啟用 enablePeriodicRefresh。這會啟用定期叢集拓撲更新,以便用戶端以 的間隔更新叢集拓撲 refreshPeriod (預設:60 秒)。停用時,用戶端只有在嘗試對叢集執行命令發生錯誤時,叢集拓撲才會更新。

啟用此選項後,您可以將此工作新增至背景任務,以減少與重新整理叢集拓撲相關的延遲。雖然拓撲重新整理是在背景工作中執行,但對於具有許多節點的叢集而言,可能會有些慢。這是因為所有節點都在查詢其檢視以取得最新的叢集檢視。如果您執行的是大型叢集,則可能需要增加期間。

啟用 enableAllAdaptiveRefreshTriggers。這會啟用適應性拓撲重新整理,其使用所有觸發條件:MOVED_REDIRECT、ASK_REDIRECT、PERSISTENT_RECONNECTS、UNCOVERED_SLOT、UNKNOWN_NODE。自適應重新整理觸發程序會根據 Valkey 或 Redis OSS叢集操作期間發生的事件啟動拓撲檢視更新。啟用此選項會在上述觸發程序發生時立即重新整理拓撲。適應性觸發程序重新整理會使用逾時來限制速率,因為事件可能會大規模發生 (更新之間的預設逾時:30)。

啟用 closeStaleConnections。如此可在重新整理叢集拓樸時關閉過時的連線。只有在 ClusterTopologyRefreshOptions.isPeriodicRefreshEnabled() 為 true 時,才會生效。啟用後,用戶端可以關閉過時的連線,並在背景建立新連線。這可減少在命令執行期間處理失敗連線的需求。

啟用 dynamicRefreshResources。我們建議為小型叢集啟用 dynamicRefreshResources ,並為大型叢集停用它。 dynamicRefreshResources 啟用從提供的種子節點探索叢集節點 (例如,叢集組態端點)。它會使用所有探索到的節點做為重新整理叢集拓撲的來源。

使用動態重新整理查詢所有探索到的叢集拓撲節點,並嘗試選擇最準確的叢集檢視。如果其設定為 false,則只會使用初始種子節點做為拓撲探索的來源,而且只會取得初始種子節點的用戶端數量。在停用時,如果叢集配置端點解析為故障的節點,則嘗試重新整理叢集檢視會失敗,並導致例外狀況。這種情況可能會發生,因為從叢集組態端點移除故障節點的項目需要一些時間。因此,組態端點仍可在短時間內隨機解析為故障的節點。

但是,在其啟用後,我們會使用從叢集檢視接收到的所有叢集節點,來查詢其目前的檢視。因為我們會從該檢視中篩選出故障的節點,所以拓撲重新整理將會成功。但是,如果 dynamicRefreshSources 為 true,Letce 會查詢所有節點以取得叢集檢視,然後比較結果。因此,對於具有大量節點的叢集來說,它可能很昂貴。建議您在多節點的叢集時關閉此功能。

final ClusterTopologyRefreshOptions topologyOptions = ClusterTopologyRefreshOptions.builder() .enableAllAdaptiveRefreshTriggers() .enablePeriodicRefresh() .dynamicRefreshSources(true) .build();

ClientResources

DnsResolver 使用 設定 DirContextDnsResolver。DNS 解析程式是以 Java 的 com.sun.jndi.dns 為基礎DnsContextFactory。

reconnectDelay 使用指數退避和全抖動來設定 。Lettuce 具有以指數退避策略為基礎的內建重試機制。如需詳細資訊,請參閱 AWS 架構部落格上的指數退避和抖動。如需重試退避策略重要性的詳細資訊,請參閱 AWS 資料庫部落格上最佳實務部落格文章的退避邏輯區段。

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、、KEYS、 SMEMBERS或 Lua FLUSHALL指令碼。針對單一金鑰命令使用較短的逾時,例如 GET、 SET和 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();