Configuração do cliente do Lettuce (Valkey e Redis OSS) - Amazon ElastiCache

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Configuração do cliente do Lettuce (Valkey e Redis OSS)

Esta seção descreve as opções de configuração recomendadas de Java e Lettuce e como elas se aplicam aos clusters do ElastiCache.

As recomendações nesta seção foram testadas com o Lettuce versão 6.2.2.

TTL de cache de DNS do Java

A JVM armazena em cache pesquisas de nome DNS. Ao resolver um nome de host para um endereço IP, a JVM armazena em cache o endereço IP para um período especificado, conhecido como Time-To-Live (TTL – Tempo de duração).

A escolha de um valor TTL envolve um equilíbrio entre latência e capacidade de resposta a mudança. Com TTLs mais curtos, os resolvedores de DNS percebem atualizações no DNS do cluster com maior rapidez. Isso pode fazer com que sua aplicação responda com maior rapidez às substituições ou a outros fluxos de trabalho pelos quais seu cluster passa. No entanto, se o TTL for muito baixo, ele aumentará o volume da consulta, o que pode aumentar a latência de sua aplicação. Embora não exista um valor TTL correto, vale a pena considerar o tempo que você pode esperar para que uma alteração seja implementada ao definir o TTL.

Como os nós do ElastiCache utilizam entradas de nome de DNS que podem mudar, recomendamos configurar a JVM com um valor TTL de cinco a dez segundos. Isso garante que, quando o endereço IP de um nó mudar, a aplicação poderá receber e usar o novo endereço IP do recurso consultando novamente a entrada DNS.

Em algumas configurações do Java, o TTL padrão da JVM é definido de maneira que jamais atualizará entradas DNS até a JVM ser reiniciada.

Para obter detalhes sobre como definir seu TTL da JVM, consulte Como definir o TTL da JVM.

Versão do Lettuce

Recomendamos o Lettuce versão 6.2.2 ou posterior.

Endpoints

Quando você estiver usando clusters habilitados para o modo de cluster, defina o redisUri como o endpoint de configuração do cluster. A pesquisa de DNS para esse URI retorna uma lista de todos os nós disponíveis no cluster e é resolvida aleatoriamente para um deles durante a inicialização do cluster. Para obter mais detalhes sobre como a atualização de topologia funciona, consulte dynamicRefreshResources mais adiante neste tópico.

SocketOption

Habilite o KeepAlive. Quando habilitada, essa opção reduz a necessidade de lidar com falhas nas conexões durante o runtime de comando.

Defina Tempo limite de conexão com base nos requisitos da aplicação e na workload. Para ter mais informações, consulte “Tempos limite” posteriormente neste tópico.

ClusterClientOption: opções do cliente habilitadas para o modo de cluster

Habilite AutoReconnect quando a conexão for perdida.

Defina CommandTimeout. Para obter mais detalhes, consulte a seção “Tempos limite” mais adiante neste tópico.

Defina nodeFilter para filtrar os nós com falha da topologia. O Lettuce salva todos os nós encontrados na saída “nós de cluster” (como nós com o status PFAIL/FAIL) nas “partições” do cliente (também conhecidas como fragmentos). Durante o processo de criação da topologia do cluster, ele tenta se conectar a todos os nós da partição. Esse comportamento do Lettuce ao adicionar nós com falha pode causar erros de conexão (ou avisos) quando os nós são substituídos por algum motivo.

Por exemplo, depois que um failover é concluído e o cluster inicia o processo de recuperação, enquanto a topologia do cluster é atualizada, o mapa de nós de barramento do cluster tem um curto período em que o nó inativo é listado como um nó com o status FAIL antes de ser completamente removido da topologia. Durante esse período, o cliente do Lettuce o considera um nó íntegro e se conecta de forma contínua a ele. Isso causa uma falha após o término da nova tentativa.

Por exemplo:

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

A filtragem de nós é mais bem utilizada com DynamicRefreshSources definido como verdadeiro. Do contrário, se a visualização da topologia for obtida de um único nó inicial problemático, que vê um nó primário de algum fragmento com o status de falha, ela filtrará esse nó primário, o que causará a falta de cobertura dos slots. Ter vários nós iniciais (quando DynamicRefreshSources é verdadeiro) reduz a probabilidade de ocorrência desse problema, pois pelo menos alguns dos nós iniciais devem ter uma visualização de topologia atualizada após um failover com o primário recém-promovido.

ClusterTopologyRefreshOptions: opções para controlar a atualização da topologia do cluster do cliente Habilitado no modo do cluster

nota

Os clusters com modo de cluster desabilitado não dão suporte a comandos de descoberta do cluster nem são compatíveis com a funcionalidade de descoberta da topologia dinâmica de todos os clientes.

O modo de cluster desabilitado com o ElastiCache não é compatível com MasterSlaveTopologyRefresh do Lettuce. Em vez disso, para o modo de cluster desabilitado, você pode configurar um StaticMasterReplicaTopologyProvider e fornecer os endpoints de leitura e gravação do cluster.

Para obter mais informações sobre a conexão com clusters no modo de cluster desabilitado, consulte Localização de endpoints de um cluster do Valkey ou do Redis OSS (modo cluster desabilitado) (console).

Se quiser usar a funcionalidade de descoberta da topologia dinâmica do Lettuce, você poderá criar um cluster com modo de cluster habilitado com a mesma configuração de fragmento do cluster existente. No entanto, para clusters habilitados no modo de cluster, recomendamos configurar pelo menos três fragmentos com no mínimo uma réplica para oferecer compatibilidade com failover rápido.

Habilite enablePeriodicRefresh. Isso possibilita atualizações periódicas da topologia do cluster para que o cliente a atualize nos intervalos do refreshPeriod (padrão: 60 segundos). Quando desabilitado, o cliente atualiza a topologia do cluster somente quando ocorrem erros ao tentar executar comandos no cluster.

Com essa opção habilitada, você pode reduzir a latência associada à atualização da topologia do cluster adicionando esse trabalho a uma tarefa em segundo plano. Embora a atualização da topologia seja executada em um trabalho em segundo plano, ela pode ser um pouco lenta para clusters com muitos nós. Isso ocorre porque todos os nós estão sendo consultados para obter a visualização mais atualizada do cluster. Se você executa um cluster grande, é recomendável aumentar o período.

Habilite enableAllAdaptiveRefreshTriggers. Isso possibilita a atualização adaptativa da topologia que usa todos os acionadores: MOVED_REDIRECT, ASK_REDIRECT, PERSISTENT_RECONNECTS, UNCOVERED_SLOT, UNKNOWN_NODE. Os acionadores de atualização adaptável iniciam atualizações de visualização da topologia com base em eventos que ocorrem durante as operações de cluster do Valkey ou Redis OSS. Quando habilitada, essa opção ocasiona uma atualização imediata da topologia quando ocorre um dos acionadores anteriores. As atualizações adaptáveis acionadas têm uma taxa limitada usando um tempo limite porque os eventos podem ocorrer em grande escala (tempo limite padrão entre as atualizações: 30).

Habilite closeStaleConnections. Isso possibilita fechar conexões obsoletas ao atualizar a topologia do cluster. Ela só entrará em vigor se ClusterTopologyRefreshOptions.isPeriodicRefreshEnabled() for verdadeiro. Quando habilitado, o cliente pode fechar conexões obsoletas e criar outras em segundo plano. Isso reduz a necessidade de lidar com falhas nas conexões durante o runtime de comando.

Habilite dynamicRefreshResources. Recomendamos habilitar a opção dynamicRefreshResources para clusters pequenos e desabilitá-la para clusters grandes. A opção dynamicRefreshResources possibilita descobrir nós de cluster com base no nó inicial fornecido (por exemplo, endpoint de configuração de cluster). Ele usa todos os nós descobertos como fontes para atualizar a topologia do cluster.

A atualização dinâmica consulta todos os nós descobertos para a topologia do cluster e tenta escolher a visualização mais precisa do cluster. Se for definido como falso, somente os nós iniciais serão usados como fontes para a descoberta da topologia e o número de clientes será obtido apenas para os nós iniciais. Quando desabilitado, se o endpoint de configuração do cluster for resolvido em um nó com falha, a tentativa de atualizar a visualização do cluster falhará e causará exceções. Esse cenário pode ocorrer porque demora até que a entrada de um nó com falha seja removida do endpoint de configuração do cluster. Portanto, o endpoint de configuração ainda pode ser resolvido aleatoriamente em um nó com falha por um curto período.

No entanto, quando está habilitado, usamos todos os nós do cluster recebidos da visualização do cluster para consultar a visualização atual. Como filtramos os nós com falha nessa visualização, a atualização da topologia será bem-sucedida. No entanto, quando dynamicRefreshSources é verdadeiro, o Lettuce consulta todos os nós para obter a visualização do cluster e, depois, compara os resultados. Portanto, isso pode ser caro para clusters com muitos nós. Sugerimos que você desabilite esse recurso para clusters com muitos nós.

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

ClientResources

Configure DnsResolver com DirContextDnsResolver. O resolvedor de DNS é baseado em com.sun.jndi.dns.DnsContextFactory do Java.

Configure reconnectDelay com recuo exponencial e oscilação total. O Lettuce tem mecanismos de repetição integrados com base nas estratégias de recuo exponencial. Para obter detalhes, consulte a publicação Recuo exponencial e oscilação no Blog de arquitetura da AWS. Para ter mais informações sobre a importância de tentar novamente uma estratégia de recuo, consulte as seções de lógica de recuo da publicação do blog sobre práticas recomendadas no Blog de bancos de dados da 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();

Tempos limite

Use um valor de tempo limite de conexão menor do que o tempo limite do comando. O Lettuce utiliza uma conexão preguiçosa. Portanto, se o tempo limite de conexão for maior do que o tempo limite do comando, você poderá ter um período de falha persistente após uma atualização da topologia se o Lettuce tentar se conectar a um nó não íntegro e o tempo limite do comando sempre for excedido.

Utilize um tempo limite de comando dinâmico para comandos diferentes. Recomendamos definir o tempo limite do comando com base na duração esperada do comando. Por exemplo, utilize um tempo limite maior para comandos que iteram em várias teclas, como scripts FLUSHDB, FLUSHALL, KEYS, SMEMBERS ou Lua. Utilize tempos limite mais curtos para comandos de tecla única, como SET, GET e HSET.

nota

Os tempos limite configurados no exemplo a seguir são para testes que executaram comandos SET/GET com chaves e valores de até 20 bytes. O tempo de processamento pode ser maior quando os comandos são complexos ou quando as chaves e os valores são maiores. Você deve definir os tempos limite com base no caso de uso de sua aplicação.

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