Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.
Descubrimiento de clientes en clústeres y retroceso exponencial (Valkey y Redis) OSS
Al conectarse a un clúster de ElastiCache Valkey o Redis con el modo de OSS clúster activado, la biblioteca cliente correspondiente debe ser compatible con los clústeres. Los clientes deben obtener un mapa de los slots hash de los nodos correspondientes del clúster para poder enviar las solicitudes a los nodos correctos, y evitar así la sobrecarga de rendimiento que supone gestionar las redirecciones del clúster. Como resultado, el cliente debe descubrir (o detectar) una lista completa de los slots y los nodos mapeados en dos situaciones diferentes:
El cliente se inicializa y debe completar la configuración inicial de los slots.
Se recibe una MOVED redirección del servidor, por ejemplo, cuando se produce una conmutación por error, cuando todas las ranuras servidas por el nodo principal anterior pasan a manos de la réplica, o se vuelve a fragmentar cuando las ranuras se mueven del nodo principal de origen al nodo principal de destino
Por lo general, la detección de clientes se realiza mediante la emisión de un CLUSTER NODE comando CLUSTER SLOT or al servidor Valkey o Redis. OSS Recomendamos CLUSTER SLOT este método porque devuelve al cliente el conjunto de rangos de ranuras y los nodos principal y de réplica asociados. Es un método que no requiere un análisis adicional por parte del cliente y es más eficaz.
Según la topología del clúster, el tamaño de la respuesta al CLUSTER SLOT comando puede variar en función del tamaño del clúster. Los clústeres más grandes y con más nodos producen una respuesta mayor. Por lo tanto, es importante asegurarse de que la cantidad de clientes que llevan a cabo la detección de la topología del clúster no aumente de forma ilimitada. Por ejemplo, cuando la aplicación cliente se inicia o pierde la conexión con el servidor y debe realizar una detección de clústeres, un error común es que la aplicación cliente desencadene varias solicitudes de reconexión y detección sin añadir un retroceso exponencial con el nuevo intento. Esto puede hacer que el OSS servidor Valkey o Redis deje de responder durante un período prolongado, con una utilización del 100%. CPU La interrupción se prolonga si cada CLUSTER SLOT comando debe procesar una gran cantidad de nodos en el bus del clúster. Hemos observado varias interrupciones en los clientes en el pasado debido a este comportamiento en varios lenguajes diferentes, incluidos Python (redis-py-cluster) y Java (Lettuce y Redisson).
En una caché sin servidor, muchos de los problemas se mitigan automáticamente porque la topología de clúster expuesta es estática y consta de dos entradas: un punto de conexión de escritura y otro de lectura. La detección de clústeres también se distribuye automáticamente entre varios nodos cuando se utiliza el punto de conexión de la caché. Sin embargo, las siguientes recomendaciones siguen siendo útiles.
A fin de mitigar el impacto causado por una entrada repentina de solicitudes de conexión y detección, recomendamos lo siguiente:
Implemente un grupo de conexiones de cliente con un tamaño finito a fin de limitar el número de conexiones entrantes simultáneas desde la aplicación cliente.
Cuando el cliente se desconecte del servidor debido al tiempo de espera, vuelva a intentarlo con retroceso exponencial y fluctuación. Esto evitará que varios clientes sobrecarguen el servidor al mismo tiempo.
Utilice la guía en Búsqueda de puntos finales de conexión en ElastiCache para encontrar el punto de conexión del clúster que necesitará a fin de realizar la detección del clúster. De este modo, distribuirá la carga de detección entre todos los nodos del clúster (hasta 90), en lugar de centrarse en unos pocos nodos raíz codificados del clúster.
A continuación, se muestran algunos ejemplos de código de la lógica de reintentos de retroceso exponencial en redis-py y Lettuce. PHPRedis
Ejemplo 1 de lógica de retroceso: redis-py
Redis-py tiene un mecanismo de reintento incorporado: se lleva a cabo un reintento inmediatamente después de un error. Este mecanismo se puede activar mediante el retry_on_timeout
argumento que se proporciona al crear un objeto Redis. OSS
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
Luego, puede utilizar el siguiente código para establecer un valor:
client = redis.Redis(connection_pool=redis.BlockingConnectionPool(host=HOST, max_connections=10)) res = run_with_backoff(lambda: client.set("key", "value")) print(res)
En función de la carga de trabajo, es posible que desee cambiar el valor del retroceso base (de 1 segundo a unas pocas decenas o cientos de milisegundos) para las cargas de trabajo sensibles a la latencia.
Ejemplo 2 de lógica de retroceso: PHPRedis
PHPRedistiene un mecanismo de reintento integrado que lo reintenta un máximo de 10 veces (no configurable). Hay un retraso configurable entre los intentos (con una fluctuación a partir del segundo reintento). Para obtener más información, consulte el siguiente código de muestra
$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);
Ejemplo 3 de lógica de retroceso: Lettuce
Lettuce tiene mecanismos de reintento incorporados que emplean las estrategias de retroceso exponencial descritas en la publicación Exponential Backoff and Jitter
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(); } } }