Lettuce 客户端配置(Valkey 和 Redis)OSS - 亚马逊 ElastiCache

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

Lettuce 客户端配置(Valkey 和 Redis)OSS

本节介绍推荐的 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为最低 TTL 5 到 10 秒。这样可以确保当节点的 IP 地址更改时,您的应用程序将能够通过重新查询该条目来接收和使用该DNS资源的新 IP 地址。

在某些 Java 配置中,会设置JVMTTL默认值,因此在重新启动之前,它永远不会刷新DNS条目。JVM

有关如何设置的详细信息 JVMTTL,请参阅如何设置JVMTTL

Lettuce 版本

我们建议使用 Lettuce 版本 6.2.2 或更高版本。

端点

当您使用启用集群模式的集群时,将 redisUri 设置为集群配置终端节点。对此的DNS查找会URI返回集群中所有可用节点的列表,并在集群初始化期间随机解析为其中一个节点。有关拓扑刷新工作原理的更多详细信息,请参阅本主题dynamicRefreshResources后面的部分。

SocketOption

启用KeepAlive。启用此选项可减少在命令运行时期间处理失败连接的需求。

确保根据应用程序要求和工作负载设置连接超时。有关更多信息,请参阅本主题后面的“超时”部分。

ClusterClientOption: 启用集群模式的客户端选项

连接中断AutoReconnect时启用。

设置CommandTimeout。有关更多详细信息,请参阅本主题后面的“超时”部分。

设置nodeFilter为从拓扑中筛选出故障节点。Lettuce 将在 “集群节点” 输出中找到的所有节点(包括FAIL状态为 PFAIL /的节点)保存在客户端的 “分区”(也称为分片)中。在创建群集拓扑的过程中,它会尝试连接到所有分区节点。当节点因任何原因被替换时,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:用于控制启用集群模式的客户端的群集拓扑刷新的选项

注意

已禁用集群模式的集群不支持集群发现命令,并且与所有客户端的动态拓扑发现功能不兼容。

禁用的集群模式与 L ElastiCache ettuce 的集群模式不兼容。MasterSlaveTopologyRefresh相反,如果禁用了集群模式,则可以配置 StaticMasterReplicaTopologyProvider 并提供集群读取和写入端点。

有关连接到已禁用集群模式的集群的更多信息,请参阅查找 Valkey 或 RedisOSS(已禁用集群模式)集群的终端节点(控制台)

如果您想使用 Lettuce 的动态拓扑发现功能,则可以使用与现有集群相同的分片配置创建启用集群模式的集群。但是,对于启用集群模式的集群,我们建议至少配置 3 个分片以及至少一个副本,以支持快速失效转移。

启用enablePeriodicRefresh。这将启用定期群集拓扑更新,以便客户端按间隔refreshPeriod (默认值:60 秒)更新集群拓扑。如果禁用,则只有在客户端尝试对集群运行命令时出现错误的情况下,才会更新集群拓扑。

启用此选项后,您可以通过将此作业添加到后台任务来减少与刷新集群拓扑相关的延迟。虽然拓扑刷新是在后台作业中执行的,但对于具有多个节点的集群来说,拓扑刷新可能会有些慢。这是因为将会查询所有节点的视图以获取最新的集群视图。如果您运行大型集群,则可能需要延长间隔。

启用enableAllAdaptiveRefreshTriggers。这将启用使用所有触发器的自适应拓扑刷新:MOVED_ REDIRECT、ASK _ REDIRECT、PERSISTENT _ RECONNECTS、UNCOVERED _、_ SLOT、UNKNOWN _ NODE。自适应刷新触发器根据 Valkey 或 Redis OSS 集群操作期间发生的事件启动拓扑视图更新。当发生上述触发器之一时,启用此选项会导致立即刷新拓扑。自适应触发刷新使用超时限制速率,因为事件可能会大规模发生(更新之间的默认超时时间为 30)。

启用closeStaleConnections。这可以在刷新集群拓扑时关闭过时的连接。它只有在以下情况下才会生效ClusterTopologyRefreshOptions。 isPeriodicRefresh启用 () 为真。启用此选项后,客户端可以关闭过时的连接并在后台创建新连接。这减少了在命令运行时期间处理失败连接的需求。

启用dynamicRefreshResources。我们建议 dynamicRefreshResources为小型集群启用,对大型集群禁用。 dynamicRefreshResources允许从提供的种子节点(例如,集群配置终端节点)发现群集节点。它使用所有发现的节点作为刷新集群拓扑的源。

使用动态刷新查询所有已发现的集群拓扑节点,并尝试选择最准确的集群视图。如果将其设置为 false,则仅使用初始种子节点作为拓扑发现的源,并且仅获取初始种子节点的客户端数量。禁用后,如果将集群配置终端节点解析为故障节点,则尝试刷新集群视图会失败并导致异常。之所以会发生这种情况,是因为从集群配置端点中删除故障节点的条目需要一些时间。因此,仍然会在短时间内将配置终端节点随机解析为故障节点。

但是,启用后,我们会使用从集群视图接收到的所有集群节点来查询其当前视图。因为我们从该视图中筛选掉了故障的节点,所以拓扑刷新将会成功。但是,如果dynamicRefreshSources 为 true,Lettuce 会查询所有节点以获取集群视图,然后比较结果。因此,对于拥有大量节点的集群来说,这可能会很昂贵。我们建议您为具有多个节点的集群关闭此功能。

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 尝试连接到不正常的节点并且总是超过命令超时,则拓扑刷新后可能会持续失败一段时间。

对不同的命令使用动态命令超时。建议您根据命令预期时长设置命令超时。例如,对迭代多个键的命令(例如、、FLUSHDBFLUSHALLKEYSSMEMBERS、或 Lua 脚本)使用更长的超时时间。对单键命令(例如、和)使用较短的SET超时时间。GET HSET

注意

以下示例中配置的超时适用于使用长度不超过 20 字节的键和值运行SET/GET命令的测试。当命令较复杂或键和值较大时,处理时间可能会更长。您应该根据应用程序的用例设置超时。

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();