¿Cómo conectar el clúster AWS Elasticache Redis a la aplicación Spring Boot?

8

Tengo la aplicación Spring Boot que se conecta al clúster de Redis, usando Jedis Connection Factory:

RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(redisProperties.getCluster().getNodes());
redisClusterConfiguration.setPassword(redisProperties.getPassword());
jedisConnectionFactory = new JedisConnectionFactory(redisClusterConfiguration);

y leyendo la lista de nodos de application.yml:

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    timeout: 300s
    cluster:
      nodes: 127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382

Ahora queremos cambiar a Elasticache ya que estamos alojando nuestro clúster Redis en AWS de todos modos. Se haría con bastante facilidad. Si se pudiera utilizar AmazonElastiCache lib. Luego, podríamos conectarnos al clúster Elasticache con credenciales de AWS, extraer los nodos disponibles, ponerlo en la lista y pasarlo a Jedis en lugar de codificarlos en application.yml, como:

//get cache cluster nodes using AWS api
private List<String> getClusterNodes(){
    AmazonElastiCache client = AmazonElastiCacheClientBuilder.standard().withRegion(Regions.DEFAULT_REGION).build();
    DescribeCacheClustersRequest describeCacheClustersRequest = new DescribeCacheClustersRequest();
    describeCacheClustersRequest.setShowCacheNodeInfo(true);
    List<CacheCluster> cacheClusterList = client.describeCacheClusters(describeCacheClustersRequest).getCacheClusters();
    List<String> nodeList = new ArrayList<>();
    try {
        for (CacheCluster cacheCluster : cacheClusterList) {
            for(CacheNode cacheNode :cacheCluster.getCacheNodes()) {
                String nodeAddr = cacheNode.getEndpoint().getAddress() + ":" +cacheNode.getEndpoint().getPort();
                nodeList.add(nodeAddr);
            }
        }
    }
    catch(Exception e) {
        e.printStackTrace();
    }
    return nodeList;
}

Pero el equipo de DevOps dijo que no pueden configurar el acceso a AWS en todos los laboratorios y tienen razones para ello. Además, en lugar de conectarse a AWS y extraer todos los clústeres disponibles, debemos conectarnos a uno específico por URL.

Así que intenté pasar la url del clúster Elasticache directamente a Jedis como independiente y como un clúster en la configuración de application.yml. En ambos casos, se establece la conexión, pero cuando la aplicación intenta escribir en Elasticache, se mueve la excepción:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.data.redis.ClusterRedirectException: Redirect: slot 1209 to 10.10.10.011:6379.; nested exception is redis.clients.jedis.exceptions.JedisMovedDataException: MOVED 1209 10.10.10.102:6379

Lo que, según tengo entendido, significa que la aplicación intentó escribir en uno de los nodos de Elasticache, pero no pudo conectarse.

Entonces, la pregunta sería ¿hay alguna manera de conectarse al clúster Elasticache Redis desde la aplicación Spring Boot utilizando solo la URL del clúster Elasticache?

Sé que es factible si se usa Elasticache Memecache. Además, el conductor Jedis no es un requisito difícil.

Gracias.

Marius Jaraminas
fuente

Respuestas:

2

Después de algunas investigaciones, aprendimos que si el punto final del clúster de Elasticche de AWS se establece como un nodo en el RedisClusterConfigurationcontrolador (Jedis o Lettuce) puede conectarse y encontrar todos los nodos en un clúster de Elasticache. Además, si uno de los nodos falla, el controlador puede comunicarse con el clúster Elasticache a través de otro nodo.

También migramos al controlador de Lettuce mientras trabajábamos en esta actualización, ya que Lettuce es el controlador predeterminado proporcionado en Spring Boot Redis Started y es compatible con las últimas versiones de Redis. Las conexiones de lechuga están diseñadas para ser seguras con hilos también, Jedis no.

Ejemplo de código:

List<String> nodes = Collections.singletonList("****.***.****.****.cache.amazonaws.com:6379");
RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration(nodes);
return new LettuceConnectionFactory(clusterConfiguration);
Marius Jaraminas
fuente
es "" ****. ***. ****. ****. cache.amazonaws.com: "un nombre de host de clúster elástico?
Santh
sí, ese es el nombre de host del clúster ElastiCache
Marius Jaraminas
0

Inspirado en la respuesta anterior:, complete el código más detallado

List<String> nodes = Collections.singletonList("<cluster-host-name>:<port>");
RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration(nodes);

ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder().closeStaleConnections(true)
            .enableAllAdaptiveRefreshTriggers().build();

ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder().autoReconnect(true)
            .topologyRefreshOptions(topologyRefreshOptions).validateClusterNodeMembership(false)
            .build();
//If you want to add tuning options
LettuceClientConfiguration  lettuceClientConfiguration = LettuceClientConfiguration.builder().readFrom(ReadFrom.REPLICA_PREFERRED).clientOptions(clusterClientOptions).build();

LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisClusterConfiguration, lettuceClientConfiguration);
lettuceConnectionFactory.afterPropertiesSet();//**this is REQUIRED**
StringRedisTemplate redisTemplate = new StringRedisTemplate(lettuceConnectionFactory);
Santh
fuente