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á.
Detecção de clientes em cluster e recuo exponencial (Valkey e Redis) OSS
Ao se conectar a um OSS cluster ElastiCache Valkey ou Redis no modo de cluster ativado, 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 MOVED redirecionamento é recebido do servidor, como na situação de um failover, quando todos os slots atendidos pelo antigo nó primário são assumidos pela réplica, ou de fragmentação novamente, quando os slots são movidos do nó primário de origem para o nó primário de destino
A descoberta do cliente geralmente é feita por meio da emissão de um CLUSTER NODE comando CLUSTER SLOT or para o servidor Valkey ou RedisOSS. Recomendamos o CLUSTER SLOT método porque ele retorna o conjunto de intervalos de slots e os nós primários e de réplica associados de volta 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 CLUSTER SLOT comando 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 com que o OSS servidor Valkey ou Redis não responda por um período prolongado de tempo, com a CPU utilização em 100%. A interrupção é prolongada se cada CLUSTER SLOT comando precisar processar um grande número de nós no barramento do cluster. No passado, observamos várias interrupções de clientes devido a esse comportamento em várias linguagens diferentes, incluindo 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 Encontrando pontos de extremidade de conexão em ElastiCache 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.
A seguir estão alguns exemplos de código para a lógica de repetição de retrocesso exponencial em redis-py e Lettuce. PHPRedis
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 ativado por meio do retry_on_timeout
argumento fornecido ao criar um OSS objeto Redis
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 de lógica de recuo: PHPRedis
PHPRedistem um mecanismo de repetição integrado que tenta novamente no máximo 10 vezes (não configurável). 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
$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
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(); } } }