本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
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将 5 到 10 秒的最低TTL配置为 5 到 10 秒。这样可以确保当节点的 IP 地址更改时,您的应用程序将能够通过重新查询该条目来接收和使用该DNS资源的新 IP 地址。
在某些 Java 配置中,会设置JVMTTL默认值,因此在重新启动之前,它永远不会刷新DNS条目。JVM
有关如何设置的详细信息 JVMTTL,请参阅如何设置JVMTTL。
Lettuce 版本
我们建议使用 Lettuce 版本 6.2.2 或更高版本。
端点
当您使用启用集群模式的集群时,将 redisUri
设置为集群配置终端节点。对此的DNS查找会URI返回集群中所有可用节点的列表,并在集群初始化期间随机解析为其中一个节点。有关拓扑刷新工作原理的更多详细信息,请参阅本主题dynamicRefreshResources后面的部分。
SocketOption
Enable 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 为 true)可以降低出现此问题的可能性,因为在使用新升级的主节点进行故障转移后,至少有一些种子节点应该具有更新的拓扑视图。
ClusterTopologyRefreshOptions:用于控制启用集群模式的客户端的群集拓扑刷新的选项
注意
已禁用集群模式的集群不支持集群发现命令,并且与所有客户端的动态拓扑发现功能不兼容。
禁用的集群模式与 L ElastiCache ettuce 的集群模式不兼容。MasterSlaveTopologyRefresh
相反,如果禁用了集群模式,则可以配置 StaticMasterReplicaTopologyProvider
并提供集群读取和写入端点。
有关连接到已禁用集群模式的集群的更多信息,请参阅查找 Valkey 或 RedisOSS(已禁用集群模式)集群的终端节点(控制台)。
如果您想使用 Lettuce 的动态拓扑发现功能,则可以使用与现有集群相同的分片配置创建启用集群模式的集群。但是,对于启用集群模式的集群,我们建议至少配置 3 个分片以及至少一个副本,以支持快速失效转移。
Enable enablePeriodicRefresh
启用此选项后,您可以通过将此作业添加到后台任务来减少与刷新集群拓扑相关的延迟。虽然拓扑刷新是在后台作业中执行的,但对于具有多个节点的集群来说,拓扑刷新可能会有些慢。这是因为将会查询所有节点的视图以获取最新的集群视图。如果您运行大型集群,则可能需要延长间隔。
Enable enableAllAdaptiveRefreshTriggers
Enable closeStaleConnections
Enable dynamicRefreshResources
使用动态刷新查询所有已发现的集群拓扑节点,并尝试选择最准确的集群视图。如果将其设置为 false,则仅使用初始种子节点作为拓扑发现的源,并且仅获取初始种子节点的客户端数量。禁用后,如果将集群配置终端节点解析为故障节点,则尝试刷新集群视图会失败并导致异常。之所以会发生这种情况,是因为从集群配置端点中删除故障节点的条目需要一些时间。因此,仍然会在短时间内将配置终端节点随机解析为故障节点。
但是,启用后,我们会使用从集群视图接收到的所有集群节点来查询其当前视图。因为我们从该视图中筛选掉了故障的节点,所以拓扑刷新将会成功。但是,如果dynamicRefreshSources 为 true,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 尝试连接到不正常的节点并且总是超过命令超时,则拓扑刷新后可能会持续失败一段时间。
对不同的命令使用动态命令超时。建议您根据命令预期时长设置命令超时。例如,对迭代多个键的命令(例如、、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();