

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á.

# Práticas recomendadas para clientes (Valkey e Redis OSS)
<a name="BestPractices.Clients.redis"></a>

Aprenda as práticas recomendadas para cenários comuns e acompanhe exemplos de código de algumas das bibliotecas de cliente Valkey e Redis OSS de código aberto mais populares (redis-py, PHPRedis e Lettuce), bem como as práticas recomendadas para interagir com recursos do ElastiCache com bibliotecas de cliente Memcached de código aberto comumente usadas.

**Topics**
+ [

# Grande número de conexões (Valkey e Redis OSS)
](BestPractices.Clients.Redis.Connections.md)
+ [

# Descoberta de cliente do cluster e recuo exponencial (Valkey e Redis OSS)
](BestPractices.Clients.Redis.Discovery.md)
+ [

# Configurar um tempo limite do lado do cliente (Valkey e Redis OSS)
](BestPractices.Clients.Redis.ClientTimeout.md)
+ [

# Configurar um tempo limite de inatividade do lado do servidor (Valkey e Redis OSS)
](BestPractices.Clients.Redis.ServerTimeout.md)
+ [

# Scripts Lua
](BestPractices.Clients.Redis.LuaScripts.md)
+ [

# Armazenamento de itens compostos grandes (Valkey e Redis OSS)
](BestPractices.Clients.Redis.LargeItems.md)
+ [

# Configuração do cliente do Lettuce (Valkey e Redis OSS)
](BestPractices.Clients-lettuce.md)
+ [

## Configurar um protocolo preferencial para clusters de pilha dupla (Valkey e Redis OSS)
](#network-type-configuring-dual-stack-redis)

# Grande número de conexões (Valkey e Redis OSS)
<a name="BestPractices.Clients.Redis.Connections"></a>

Caches com tecnologia sem servidor e nós do ElastiCache para Redis OSS individuais dão suporte a até 65 mil conexões de cliente simultâneas. No entanto, para otimizar o desempenho, recomendamos que as aplicações cliente não funcionem de maneira constante nesse nível de conexões. O Valkey e o Redis OSS têm um processo de thread único baseado em um loop de eventos no qual as solicitações de cliente recebidas são processadas sequencialmente. Isso significa que o tempo de resposta de um determinado cliente aumenta à medida que o número de clientes conectados aumenta.

É possível realizar o seguinte conjunto de ações para evitar um gargalo de conexões em um servidor Valkey ou Redis OSS:
+ Realize operações de leitura a partir das réplicas de leitura. Isso pode ser feito usando-se os endpoints de leitor do ElastiCache em modo cluster desabilitado ou usando réplicas para leituras em modo de cluster habilitado, inclusive um cache sem servidor.
+ Distribua tráfego de gravação em vários nós primários. É possível fazer isso de duas maneiras. É possível usar um cluster multifragmentado do Valkey ou Redis OSS com um cliente compatível com o modo cluster. Também é possível gravar em vários nós primários em modo de cluster desabilitado com fragmentação do lado do cliente. Isso é feito automaticamente em um cache sem servidor.
+ Use um pool de conexões quando disponível na biblioteca cliente.

Em geral, criar uma conexão TCP é uma operação cara em termos computacionais em comparação com os comandos típicos do Valkey ou Redis OSS. Por exemplo, lidar com uma solicitação SET/GET é uma ordem de magnitude mais rápida ao reutilizar uma conexão existente. O uso de um pool de conexões de clientes com um tamanho finito reduz a sobrecarga do gerenciamento de conexões. Isso também limita o número de conexões de entrada simultâneas da aplicação cliente.

O seguinte exemplo de código do PHPRedis mostra que uma nova conexão é criada para cada nova solicitação do usuário:

```
$redis = new Redis();
if ($redis->connect($HOST, $PORT) != TRUE) {
	//ERROR: connection failed
	return;
}
$redis->set($key, $value);
unset($redis);
$redis = NULL;
```

Comparamos esse código em um loop em uma instância do Amazon Elastic Compute Cloud (Amazon EC2) conectada a um nó Graviton2 (m6g.2xlarge) do ElastiCache para o Redis OSS. Colocamos o cliente e o servidor na mesma zona de disponibilidade. A latência média de toda a operação foi de 2,82 milissegundos.

Quando atualizamos o código e usamos conexões persistentes e um pool de conexões, a latência média de toda a operação foi de 0,21 milissegundo:

```
$redis = new Redis();
if ($redis->pconnect($HOST, $PORT) != TRUE) {
	// ERROR: connection failed
	return;
}
$redis->set($key, $value);
unset($redis);
$redis = NULL;
```

Configurações de redis.ini necessárias:
+ `redis.pconnect.pooling_enabled=1`
+ `redis.pconnect.connection_limit=10`

O seguinte código é um exemplo de um [pool de conexões do Redis-py](https://redis.readthedocs.io/en/stable/):

```
conn = Redis(connection_pool=redis.BlockingConnectionPool(host=HOST, max_connections=10))
conn.set(key, value)
```

O seguinte código é um exemplo de um [pool de conexões do Lettuce](https://lettuce.io/core/release/reference/#_connection_pooling):

```
RedisClient client = RedisClient.create(RedisURI.create(HOST, PORT));
GenericObjectPool<StatefulRedisConnection> pool = ConnectionPoolSupport.createGenericObjectPool(() -> client.connect(), new GenericObjectPoolConfig());
pool.setMaxTotal(10); // Configure max connections to 10
try (StatefulRedisConnection connection = pool.borrowObject()) {
	RedisCommands syncCommands = connection.sync();
	syncCommands.set(key, value);
}
```

# Descoberta de cliente do cluster e recuo exponencial (Valkey e Redis OSS)
<a name="BestPractices.Clients.Redis.Discovery"></a>

Ao se conectar a um cluster do ElastiCache Valkey ou Redis OSS em modo cluster habilitado, a biblioteca cliente correspondente deve estar ciente do cluster. Os clientes devem obter um mapa dos slots de hash para os nós correspondentes no cluster para enviar solicitações aos nós certos e evitar a sobrecarga de desempenho de processar redirecionamentos de cluster. Dessa forma, o cliente deve descobrir uma lista completa de slots e dos nós mapeados em duas situações diferentes:
+ O cliente é inicializado e deve preencher a configuração de slots inicial
+ Um redirecionamento MOVED é recebido do servidor, como na situação de um failover, quando todos os slots atendidos pelo nó primário anterior são assumidos pela réplica, ou de refragmentação quando slots são movidos do nó primário de origem para o nó primário de destino

A descoberta de cliente costuma ser feita por meio da emissão de um comando CLUSTER SLOT ou CLUSTER NODE para o servidor Valkey ou Redis OSS. Recomendamos o método CLUSTER SLOT porque ele retorna o conjunto de intervalos de slots e os nós primários e de réplica reassociados ao cliente. Isso não exige análise adicional do cliente e é mais eficiente.

Dependendo da topologia do cluster, o tamanho da resposta para o comando CLUSTER SLOT pode variar com base no tamanho do cluster. Clusters maiores com mais nós produzem uma resposta maior. Assim, é importante garantir que o número de clientes que fazem a descoberta da topologia do cluster não cresça de maneira ilimitada. Por exemplo, quando a aplicação cliente é inicializada ou perde a conexão do servidor e precisa realizar a descoberta de cluster, um erro comum é que a aplicação cliente dispara várias solicitações de reconexão e descoberta sem adicionar um recuo exponencial ao tentar novamente. Isso pode fazer o servidor Valkey ou Redis OSS não responder por um período prolongado, com a utilização da CPU em 100%. A interrupção será prolongada se cada comando CLUSTER SLOT precisar processar um número grande de nós no barramento do cluster. Observamos várias interrupções de cliente por causa desse comportamento em várias linguagens diferentes, inclusive Python (redis-py-cluster) e Java (Lettuce e Redisson).

Em um cache sem servidor, muitos dos problemas são atenuados automaticamente porque a topologia do cluster anunciada é estática e consiste em duas entradas: um endpoint de gravação e um endpoint de leitura. A descoberta de cluster também é distribuída automaticamente por vários nós durante o uso do endpoint de cache. No entanto, as recomendações a seguir continuam sendo úteis.

Para mitigar o impacto causado por um fluxo repentino de solicitações de conexão e descoberta, recomendamos o seguinte:
+ Implemente um pool de conexões do cliente com um tamanho finito para limitar o número de conexões de entrada simultâneas da aplicação cliente.
+ Quando o cliente se desconectar do servidor por causa do tempo limite, tente novamente com recuo exponencial com instabilidade. Isso ajuda a evitar que vários clientes sobrecarreguem o servidor ao mesmo tempo.
+ Use o guia em [Encontrar endpoints de conexão no ElastiCache](Endpoints.md) para encontrar o endpoint do cluster a fim de realizar a descoberta do cluster. Ao fazer isso, você distribui a carga de descoberta em todos os nós do cluster (até 90) em vez de atingir alguns nós propagados codificados no cluster.

Estes são alguns exemplos de código para a lógica de nova tentativa de recuo exponencial em redis-py, phpRedis e Lettuce.

**Exemplo 1 da lógica de recuo: redis-py**

O redis-py tem um mecanismo de nova tentativa integrado repetido uma vez logo depois de uma falha. Esse mecanismo pode ser habilitado por meio do argumento `retry_on_timeout` fornecido ao criar um objeto do [Redis OSS](https://redis.readthedocs.io/en/stable/examples/connection_examples.html#redis.Redis). Aqui, demonstramos um mecanismo de nova tentativa personalizado com recuo exponencial e instabilidade. Enviamos uma solicitação pull para implementar nativamente o recuo exponencial em [redis-py (\$11494)](https://github.com/andymccurdy/redis-py/pull/1494). No futuro, talvez não seja necessário implementar manualmente.

```
def run_with_backoff(function, retries=5):
base_backoff = 0.1 # base 100ms backoff
max_backoff = 10 # sleep for maximum 10 seconds
tries = 0
while True:
try:
  return function()
except (ConnectionError, TimeoutError):
  if tries >= retries:
	raise
  backoff = min(max_backoff, base_backoff * (pow(2, tries) + random.random()))
  print(f"sleeping for {backoff:.2f}s")
  sleep(backoff)
  tries += 1
```

É possível acabar usando o seguinte código para definir um valor:

```
client = redis.Redis(connection_pool=redis.BlockingConnectionPool(host=HOST, max_connections=10))
res = run_with_backoff(lambda: client.set("key", "value"))
print(res)
```

Dependendo da workload, talvez você queira alterar o valor de recuo base de 1 segundo para algumas dezenas ou centenas de milissegundos para workloads sensíveis à latência.

**Exemplo 2 da lógica de recuo: PHPRedis**

O PHPRedis tem um mecanismo de nova tentativa integrado que repete um máximo (não configurável) de dez vezes. Há um atraso configurável entre as tentativas (com uma instabilidade a partir da segunda tentativa). Para obter mais informações, consulte o [código de exemplo](https://github.com/phpredis/phpredis/blob/b0b9dd78ef7c15af936144c1b17df1a9273d72ab/library.c#L335-L368). Enviamos uma solicitação pull para implementar nativamente o recuo exponencial em [phpRedis (\$11986)](https://github.com/phpredis/phpredis/pull/1986), que já foi mesclado e [documentado](https://github.com/phpredis/phpredis/blob/develop/README.md#retry-and-backoff). Para aqueles que estão na versão mais recente do PHPRedis, não será necessário implementar manualmente, mas incluímos aqui a referência para aqueles nas versões anteriores. Por enquanto, este é um exemplo de código que configura o atraso do mecanismo de nova tentativa:

```
$timeout = 0.1; // 100 millisecond connection timeout
$retry_interval = 100; // 100 millisecond retry interval
$client = new Redis();
if($client->pconnect($HOST, $PORT, $timeout, NULL, $retry_interval) != TRUE) {
	return; // ERROR: connection failed
}
$client->set($key, $value);
```

**Exemplo 3 da lógica de recuo: Lettuce**

O Lettuce tem mecanismos de nova tentativa integrados com base nas estratégias de recuo exponencial baseado na publicação [Recuo exponencial e instabilidade](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/). Este é um trecho de código que mostra a abordagem de instabilidade completa:

```
public static void main(String[] args)
{
	ClientResources resources = null;
	RedisClient client = null;

	try {
		resources = DefaultClientResources.builder()
				.reconnectDelay(Delay.fullJitter(
			Duration.ofMillis(100),     // minimum 100 millisecond delay
			Duration.ofSeconds(5),      // maximum 5 second delay
			100, TimeUnit.MILLISECONDS) // 100 millisecond base
		).build();

		client = RedisClient.create(resources, RedisURI.create(HOST, PORT));
		client.setOptions(ClientOptions.builder()
	.socketOptions(SocketOptions.builder().connectTimeout(Duration.ofMillis(100)).build()) // 100 millisecond connection timeout
	.timeoutOptions(TimeoutOptions.builder().fixedTimeout(Duration.ofSeconds(5)).build()) // 5 second command timeout
	.build());

	    // use the connection pool from above example
	} finally {
		if (connection != null) {
			connection.close();
		}

		if (client != null){
			client.shutdown();
		}

		if (resources != null){
			resources.shutdown();
		}

	}
}
```

# Configurar um tempo limite do lado do cliente (Valkey e Redis OSS)
<a name="BestPractices.Clients.Redis.ClientTimeout"></a>

**Configuração do tempo limite do lado do cliente**

Configure devidamente o tempo limite do lado do cliente a fim de permitir que o servidor tenha tempo suficiente para processar a solicitação e gerar a resposta. Isso também permitirá antecipar-se à falha se a conexão com o servidor não puder ser estabelecida. Determinados comandos do Valkey ou Redis OSS podem ser mais caros do que outros em termos computacionais. Por exemplo, scripts Lua ou transações MULTI/EXEC que contenham vários comandos que devem ser executados de maneira atômica. Em geral, um tempo limite do lado do cliente maior é recomendado para evitar um tempo limite do cliente antes que a resposta seja recebida do servidor, inclusive o seguinte:
+ Execução de comandos em várias teclas
+ Execução de transações MULTI/EXEC ou scripts Lua que consistem em vários comandos individuais do Valkey ou Redis OSS
+ Leitura de valores grandes
+ Realização de operações de bloqueio, como BLPOP

No caso de uma operação de bloqueio, como BLPOP, a prática recomendada é definir o tempo limite do comando como um número menor que o tempo limite do soquete.

Estes são exemplos de código para implementar um tempo limite do lado do cliente em redis-py, PHPRedis e Lettuce.

**Exemplo 1 de configuração do tempo limite: redis-py**

Este é um exemplo de código com redis-py:

```
# connect to Redis server with a 100 millisecond timeout
# give every Redis command a 2 second timeout
client = redis.Redis(connection_pool=redis.BlockingConnectionPool(host=HOST, max_connections=10,socket_connect_timeout=0.1,socket_timeout=2))

res = client.set("key", "value") # will timeout after 2 seconds
print(res)                       # if there is a connection error

res = client.blpop("list", timeout=1) # will timeout after 1 second
                                      # less than the 2 second socket timeout
print(res)
```

**Exemplo 2 de configuração do tempo limite: PHPRedis**

Este é um exemplo de código com PHPRedis:

```
// connect to Redis server with a 100ms timeout
// give every Redis command a 2s timeout
$client = new Redis();
$timeout = 0.1; // 100 millisecond connection timeout
$retry_interval = 100; // 100 millisecond retry interval
$client = new Redis();
if($client->pconnect($HOST, $PORT, 0.1, NULL, 100, $read_timeout=2) != TRUE){
	return; // ERROR: connection failed
}
$client->set($key, $value);

$res = $client->set("key", "value"); // will timeout after 2 seconds
print "$res\n";                      // if there is a connection error

$res = $client->blpop("list", 1); // will timeout after 1 second
print "$res\n";                   // less than the 2 second socket timeout
```

**Exemplo 3 de configuração do tempo limite: Lettuce**

Este é um exemplo de código com Lettuce:

```
// connect to Redis server and give every command a 2 second timeout
public static void main(String[] args)
{
	RedisClient client = null;
	StatefulRedisConnection<String, String> connection = null;
	try {
		client = RedisClient.create(RedisURI.create(HOST, PORT));
		client.setOptions(ClientOptions.builder()
	.socketOptions(SocketOptions.builder().connectTimeout(Duration.ofMillis(100)).build()) // 100 millisecond connection timeout
	.timeoutOptions(TimeoutOptions.builder().fixedTimeout(Duration.ofSeconds(2)).build()) // 2 second command timeout 
	.build());

		// use the connection pool from above example

		commands.set("key", "value"); // will timeout after 2 seconds
		commands.blpop(1, "list"); // BLPOP with 1 second timeout
	} finally {
		if (connection != null) {
			connection.close();
		}

		if (client != null){
			client.shutdown();
		}
	}
}
```

# Configurar um tempo limite de inatividade do lado do servidor (Valkey e Redis OSS)
<a name="BestPractices.Clients.Redis.ServerTimeout"></a>

Observamos casos nos quais a aplicação de um cliente tem um grande número de clientes ociosos conectados, mas não está enviando comandos ativamente. Nesses cenários, você pode esgotar todas as 65.000 conexões com um grande número de clientes inativos. Para evitar esses cenários, defina a configuração de tempo limite da maneira indicada no servidor por meio de [Parâmetros do Valkey e do Redis OSS](ParameterGroups.Engine.md#ParameterGroups.Redis). Isso garante que o servidor desconecte ativamente os clientes ociosos para evitar um aumento no número de conexões. Essa configuração não está disponível em caches sem servidor.

# Scripts Lua
<a name="BestPractices.Clients.Redis.LuaScripts"></a>

O Valkey e o Redis OSS aceitam mais de 200 comandos, inclusive aqueles para executar scripts Lua. No entanto, quando o assunto são scripts Lua, existem diversas armadilhas que podem afetar a memória e a disponibilidade do Valkey ou do Redis OSS.

**Scripts Lua não parametrizados**

Cada script Lua é armazenado em cache no servidor Valkey ou Redis OSS antes de ser executado. Os scripts Lua não parametrizados são exclusivos, o que pode fazer com que o servidor Valkey ou Redis OSS armazene um número grande de scripts Lua e consuma mais memória. Para mitigar isso, certifique-se de que todos os scripts Lua estejam parametrizados e execute regularmente o SCRIPT FLUSH para limpar scripts Lua em cache, se necessário.

Também esteja ciente de que as chaves devem ser fornecidas. Se um valor para o parâmetro KEY não for fornecido, o script falhará. Por exemplo, o seguinte não funciona: 

```
serverless-test-lst4hg.serverless.use1.cache.amazonaws.com:6379> eval 'return "Hello World"' 0
(error) ERR Lua scripts without any input keys are not supported.
```

Isso funcionará:

```
serverless-test-lst4hg.serverless.use1.cache.amazonaws.com:6379> eval 'return redis.call("get", KEYS[1])' 1 mykey-2
"myvalue-2"
```

O exemplo a seguir mostra como usar scripts parametrizados: Primeiro, temos um exemplo de uma abordagem não parametrizada que resulta em três scripts Lua em cache diferentes e não é recomendada:

```
eval "return redis.call('set','key1','1')" 0
eval "return redis.call('set','key2','2')" 0
eval "return redis.call('set','key3','3')" 0
```

Em vez disso, use o seguinte padrão para criar um único script capaz de aceitar parâmetros passados:

```
eval "return redis.call('set',KEYS[1],ARGV[1])" 1 key1 1 
eval "return redis.call('set',KEYS[1],ARGV[1])" 1 key2 2 
eval "return redis.call('set',KEYS[1],ARGV[1])" 1 key3 3
```

**Scripts Lua de longa duração**

Os scripts Lua podem executar vários comandos atomicamente, logo, podem demorar mais para serem concluídos do que um comando normal do Valkey ou Redis OSS. Se o script Lua só executar operações somente leitura, você poderá pará-lo no meio. No entanto, assim que executa uma operação de gravação, o script Lua se torna imortal e deve ser executado até a conclusão. Um script Lua de longa execução que esteja sofrendo mutação pode fazer com que o servidor Valkey ou Redis OSS deixe de responder por um longo tempo. Para mitigar esse problema, evite scripts Lua de longa duração e teste o script em um ambiente de pré-produção.

**Script Lua com gravações furtivas**

Existem algumas maneiras pelas quais um script Lua pode continuar gravando novos dados no Valkey ou Redis OSS mesmo quando o Valkey ou Redis OSS estiver acima de `maxmemory`:
+ O script começa quando o servidor Valkey ou Redis OSS está abaixo de `maxmemory` e contém várias operações de gravação dentro
+ O primeiro comando de gravação do script não está consumindo memória (como DEL), seguido de mais operações de gravação que consumam memória
+ Você pode mitigar esse problema configurando uma política de remoção adequada no servidor Valkey ou Redis OSS diferente de `noeviction`. Isso permite que o Redis OSS remova itens e libere memória entre os scripts Lua.

# Armazenamento de itens compostos grandes (Valkey e Redis OSS)
<a name="BestPractices.Clients.Redis.LargeItems"></a>

Em alguns cenários, uma aplicação pode armazenar itens compostos grandes no Valkey ou Redis OSS (como um conjunto de dados hash de vários GB). Não se trata de uma prática recomendada porque ela normalmente acarreta problemas de desempenho no Valkey ou Redis OSS. Por exemplo, o cliente pode executar um comando HGETALL para recuperar toda a coleção hash de vários GB. Isso pode gerar uma pressão de memória significativa para o servidor Valkey ou Redis OSS, armazenando em buffer o item grande no buffer de saída do cliente. Além disso, para migração de slots em modo de cluster, o ElastiCache não migra slots que contenham itens com tamanho serializado maior que 256 MB.

Para resolver os problemas de itens grandes, temos as seguintes recomendações:
+ Divida o item composto grande em vários itens menores. Por exemplo, divida uma coleção de hash grande em campos de valor-chave individuais com um esquema de nome de chave que reflita da maneira indicada a coleção, como o uso de um prefixo comum no nome da chave para identificar a coleção de itens. Se precisar acessar vários campos na mesma coleção atomicamente, você poderá usar o comando MGET para recuperar vários valores-chave no mesmo comando.
+ Se você tiver avaliado todas as opções e ainda não tiver conseguido dividir o conjunto de dados da coleção grande, tente usar comandos que operem em um subconjunto dos dados na coleção, e não em toda a coleção. Evite ter um caso de uso que exija que você recupere atomicamente toda a coleção de vários GB no mesmo comando. Um exemplo é usar os comandos HGET ou HMGET em vez de HGETALL em coleções de hash.

# Configuração do cliente do Lettuce (Valkey e Redis OSS)
<a name="BestPractices.Clients-lettuce"></a>

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.

**Topics**
+ [

# Exemplo: configuração do Lettuce para modo de cluster, TLS habilitado
](BestPractices.Clients-lettuce-cme.md)
+ [

# Exemplo: configuração do Lettuce para modo de cluster desabilitado, TLS habilitado
](BestPractices.Clients-lettuce-cmd.md)

**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](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/java-dg-jvm-ttl.html#how-to-set-the-jvm-ttl).

**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](https://lettuce.io/core/release/api/io/lettuce/core/SocketOptions.KeepAliveOptions.html). 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](https://lettuce.io/core/release/api/io/lettuce/core/SocketOptions.Builder.html#connectTimeout-java.time.Duration-) 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](https://lettuce.io/core/release/api/io/lettuce/core/cluster/ClusterClientOptions.Builder.html#autoReconnect-boolean-) quando a conexão for perdida.

Defina [CommandTimeout](https://lettuce.io/core/release/api/io/lettuPrce/core/RedisURI.html#getTimeout--). Para obter mais detalhes, consulte a seção “Tempos limite” mais adiante neste tópico.

Defina [nodeFilter](https://lettuce.io/core/release/api/io/lettuce/core/cluster/ClusterClientOptions.Builder.html#nodeFilter-java.util.function.Predicate-) 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)](Endpoints.md#Endpoints.Find.Redis).  
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](https://lettuce.io/core/release/api/io/lettuce/core/cluster/ClusterTopologyRefreshOptions.Builder.html#enablePeriodicRefresh-java.time.Duration-). 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](https://lettuce.io/core/release/api/io/lettuce/core/cluster/ClusterTopologyRefreshOptions.Builder.html#enableAllAdaptiveRefreshTriggers--). Isso possibilita a atualização adaptativa da topologia que usa todos os [acionadores](https://lettuce.io/core/6.1.6.RELEASE/api/io/lettuce/core/cluster/ClusterTopologyRefreshOptions.RefreshTrigger.html): MOVED\$1REDIRECT, ASK\$1REDIRECT, PERSISTENT\$1RECONNECTS, UNCOVERED\$1SLOT, UNKNOWN\$1NODE. 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](https://lettuce.io/core/release/api/io/lettuce/core/cluster/ClusterTopologyRefreshOptions.Builder.html#closeStaleConnections-boolean-). Isso possibilita fechar conexões obsoletas ao atualizar a topologia do cluster. Ela só entrará em vigor se [ClusterTopologyRefreshOptions.isPeriodicRefreshEnabled()](https://lettuce.io/core/release/api/io/lettuce/core/cluster/ClusterTopologyRefreshOptions.html#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](https://lettuce.io/core/release/api/io/lettuce/core/cluster/ClusterTopologyRefreshOptions.Builder.html#dynamicRefreshSources-boolean-). 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](https://lettuce.io/core/release/api/io/lettuce/core/resource/DefaultClientResources.Builder.html#dnsResolver-io.lettuce.core.resource.DnsResolver-) com [DirContextDnsResolver](https://lettuce.io/core/release/api/io/lettuce/core/resource/DirContextDnsResolver.html). O resolvedor de DNS é baseado em com.sun.jndi.dns.DnsContextFactory do Java.

Configure [reconnectDelay](https://lettuce.io/core/release/api/io/lettuce/core/resource/DefaultClientResources.Builder.html#reconnectDelay-io.lettuce.core.resource.Delay-) 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](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter) 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](https://aws.amazon.com/blogs/database/best-practices-redis-clients-and-amazon-elasticache-for-redis/) 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();
```

# Exemplo: configuração do Lettuce para modo de cluster, TLS habilitado
<a name="BestPractices.Clients-lettuce-cme"></a>

**nota**  
Os tempos limite 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. 

```
// Set DNS cache TTL
public void setJVMProperties() {
    java.security.Security.setProperty("networkaddress.cache.ttl", "10");
}

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

// Create RedisURI from the cluster configuration endpoint
clusterConfigurationEndpoint = <cluster-configuration-endpoint> // TODO: add your cluster configuration endpoint
final RedisURI redisUriCluster =
    RedisURI.Builder.redis(clusterConfigurationEndpoint)
        .withPort(6379)
        .withSsl(true)
        .build();

// Configure the client's resources                
ClientResources clientResources = DefaultClientResources.builder()
    .reconnectDelay(
        Delay.fullJitter(
            Duration.ofMillis(100),     // minimum 100 millisecond delay
            Duration.ofSeconds(10),      // maximum 10 second delay
            100, TimeUnit.MILLISECONDS)) // 100 millisecond base
    .dnsResolver(new DirContextDnsResolver())
    .build(); 

// Create a cluster client instance with the URI and resources
RedisClusterClient redisClusterClient = 
    RedisClusterClient.create(clientResources, redisUriCluster);

// Use a dynamic timeout for commands, to avoid timeouts during
// cluster management and slow operations.
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 metaCommandTimeout;
    private final Duration defaultCommandTimeout;

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

TimeoutOptions timeoutOptions = TimeoutOptions.builder()
    .timeoutSource(new DynamicClusterTimeout(DEFAULT_COMMAND_TIMEOUT, META_COMMAND_TIMEOUT))
     .build();

// Configure the topology refreshment options
final ClusterTopologyRefreshOptions topologyOptions = 
    ClusterTopologyRefreshOptions.builder()
    .enableAllAdaptiveRefreshTriggers()
    .enablePeriodicRefresh()
    .dynamicRefreshSources(true)
    .build();

// Configure the socket options
final SocketOptions socketOptions = 
    SocketOptions.builder()
    .connectTimeout(CONNECT_TIMEOUT) 
    .keepAlive(true)
    .build();

// Configure the client's options
final ClusterClientOptions clusterClientOptions = 
    ClusterClientOptions.builder()
    .topologyRefreshOptions(topologyOptions)
    .socketOptions(socketOptions)
    .autoReconnect(true)
    .timeoutOptions(timeoutOptions) 
    .nodeFilter(it -> 
        ! (it.is(RedisClusterNode.NodeFlag.FAIL) 
        || it.is(RedisClusterNode.NodeFlag.EVENTUAL_FAIL) 
        || it.is(RedisClusterNode.NodeFlag.NOADDR))) 
    .validateClusterNodeMembership(false)
    .build();
    
redisClusterClient.setOptions(clusterClientOptions);

// Get a connection
final StatefulRedisClusterConnection<String, String> connection = 
    redisClusterClient.connect();

// Get cluster sync/async commands   
RedisAdvancedClusterCommands<String, String> sync = connection.sync();
RedisAdvancedClusterAsyncCommands<String, String> async = connection.async();
```

# Exemplo: configuração do Lettuce para modo de cluster desabilitado, TLS habilitado
<a name="BestPractices.Clients-lettuce-cmd"></a>

**nota**  
Os tempos limite 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. 

```
// Set DNS cache TTL
public void setJVMProperties() {
    java.security.Security.setProperty("networkaddress.cache.ttl", "10");
}

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

// Create RedisURI from the primary/reader endpoint
clusterEndpoint = <primary/reader-endpoint> // TODO: add your node endpoint
RedisURI redisUriStandalone =
    RedisURI.Builder.redis(clusterEndpoint).withPort(6379).withSsl(true).withDatabase(0).build();

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

// Use a dynamic timeout for commands, to avoid timeouts during
// slow operations.
class DynamicTimeout extends TimeoutSource {
     private static final Set<ProtocolKeyword> META_COMMAND_TYPES = ImmutableSet.<ProtocolKeyword>builder()
          .add(CommandType.FLUSHDB)
          .add(CommandType.FLUSHALL)
          .add(CommandType.INFO)
          .add(CommandType.KEYS)
          .build();

    private final Duration metaCommandTimeout;
    private final Duration defaultCommandTimeout;

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

TimeoutOptions timeoutOptions = TimeoutOptions.builder()
    .timeoutSource(new DynamicTimeout(DEFAULT_COMMAND_TIMEOUT, META_COMMAND_TIMEOUT))
     .build();                      
                                    
final SocketOptions socketOptions =
    SocketOptions.builder().connectTimeout(CONNECT_TIMEOUT).keepAlive(true).build();

ClientOptions clientOptions =
    ClientOptions.builder().timeoutOptions(timeoutOptions).socketOptions(socketOptions).build();

RedisClient redisClient = RedisClient.create(clientResources, redisUriStandalone);
redisClient.setOptions(clientOptions);
```

## Configurar um protocolo preferencial para clusters de pilha dupla (Valkey e Redis OSS)
<a name="network-type-configuring-dual-stack-redis"></a>

Para clusters do Valkey ou Redis OSS de modo cluster habilitado, você pode controlar o protocolo que os clientes usarão para se conectar aos nós no cluster com o parâmetro de descoberta de IP. O parâmetro de descoberta de IP pode ser definido como IPv4 ou IPv6. 

Para clusters do Valkey ou Redis OSS, o parâmetro de descoberta de IP define o protocolo IP usado na saída de [cluster slots ()](https://valkey.io/commands/cluster-slots/), [cluster shards ()](https://valkey.io/commands/cluster-shards/) e [cluster nodes ()](https://valkey.io/commands/cluster-nodes/). Esses comandos são usados pelos clientes para descobrir a topologia do cluster. Os clientes usam os IPs nesses comandos para se conectar aos outros nós no cluster. 

Alterar a descoberta de IP não resultará em nenhum tempo de inatividade para os clientes conectados. No entanto, as alterações levarão algum tempo para se propagar. Para determinar quando as alterações foram completamente propagadas para um cluster do Valkey ou Redis OSS, monitore a saída de `cluster slots`. Depois que todos os nós retornados pelo comando cluster slots relatam IPs com o novo protocolo, as alterações terminam de se propagar. 

Exemplo com Redis-Py:

```
cluster = RedisCluster(host="xxxx", port=6379)
target_type = IPv6Address # Or IPv4Address if changing to IPv4

nodes = set()
while len(nodes) == 0 or not all((type(ip_address(host)) is target_type) for host in nodes):
    nodes = set()

   # This refreshes the cluster topology and will discovery any node updates.
   # Under the hood it calls cluster slots
    cluster.nodes_manager.initialize()
    for node in cluster.get_nodes():
        nodes.add(node.host)
    self.logger.info(nodes)

    time.sleep(1)
```

Exemplo com Lettuce:

```
RedisClusterClient clusterClient = RedisClusterClient.create(RedisURI.create("xxxx", 6379));

Class targetProtocolType = Inet6Address.class; // Or Inet4Address.class if you're switching to IPv4

Set<String> nodes;
    
do {
   // Check for any changes in the cluster topology.
   // Under the hood this calls cluster slots
    clusterClient.refreshPartitions();
    Set<String> nodes = new HashSet<>();

    for (RedisClusterNode node : clusterClient.getPartitions().getPartitions()) {
        nodes.add(node.getUri().getHost());
    }

    Thread.sleep(1000);
} while (!nodes.stream().allMatch(node -> {
            try {
                return finalTargetProtocolType.isInstance(InetAddress.getByName(node));
            } catch (UnknownHostException ignored) {}
            return false;
}));
```