如何将 AWS Elasticache Redis 集群连接到 Spring Boot 应用程序?

Posted

技术标签:

【中文标题】如何将 AWS Elasticache Redis 集群连接到 Spring Boot 应用程序?【英文标题】:How to connect AWS Elasticache Redis cluster to Spring Boot app? 【发布时间】:2020-04-04 22:42:19 【问题描述】:

我有使用 Jedis 连接工厂连接到 Redis 集群的 Spring Boot 应用程序:

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

并从 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

现在我们想切换到 Elasticache,因为无论如何我们都在 AWS 上托管我们的 Redis 集群。 这很容易完成。如果可以使用 AmazonElastiCache 库。 然后我们可以使用 AWS 凭证连接到 Elasticache 集群,将可用节点拉到列表中并将其传递给 Jedis,而不是在 application.yml 中硬编码它们,例如:

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

但 DevOps 团队表示,他们无法在所有实验室中配置 AWS 访问,他们有这样做的理由。此外,我们需要通过 URL 连接到特定的集群,而不是连接到 AWS 并拉取所有可用的集群。

所以我尝试将 Elasticache 集群 url 作为独立的和作为 application.yml 配置中的集群直接传递给 Jedis。 在这两种情况下都建立了连接,但是当 App 尝试写入 Elasticache 时,它​​会出现 MOVED 异常:

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

据我了解,这意味着 App 尝试写入 Elasticache 中的一个节点,但无法连接。

那么问题是有没有办法只使用 Elasticache 集群 URL 从 Spring Boot 应用程序连接到 Elasticache Redis 集群?

我知道如果使用 Elasticache Memecache 是可行的。 Jedis驱动也不是硬性要求。

谢谢。

【问题讨论】:

【参考方案1】:

经过一些研究,我们了解到如果将 AWS Elasticache 集群端点设置为 RedisClusterConfiguration 中的一个节点,那么驱动程序(Jedis 或 Lettuce)就能够连接并找到 Elasticache 集群中的所有节点。此外,如果其中一个节点出现故障,驱动程序能够通过其他节点与 Elasticache 集群通信。

我们在进行此升级时也迁移到了 Lettuce 驱动程序,因为 Lettuce 是 Spring Boot Redis Started 中提供的默认驱动程序,并支持最新的 Redis 版本。 Lettuce 连接也被设计为线程安全的,Jedis 不是。

代码示例:

List<String> nodes = Collections.singletonList("****.***.****.****.cache.amazonaws.com:6379");
RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration(nodes);
return new LettuceConnectionFactory(clusterConfiguration);

【讨论】:

是 ""****.***.****.****.cache.amazonaws.com:" 弹性集群主机名吗?【参考方案2】:

Inspired from Above Answer:,完成更详细的代码

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(clusterConfiguration, lettuceClientConfiguration);
lettuceConnectionFactory.afterPropertiesSet();//**this is REQUIRED**
StringRedisTemplate redisTemplate = new StringRedisTemplate(lettuceConnectionFactory);

【讨论】:

afterPropertiesSet 是必需的,因为您似乎没有将LettuceConncectionFactory 用作@Bean

以上是关于如何将 AWS Elasticache Redis 集群连接到 Spring Boot 应用程序?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 `redis` 通过 SSL 跨 ssh 隧道连接到 Redis 实例(AWS elasticache)?

Rails + ActionCable + Passenger + AWS Elasticache (Redis):如何修复“WebSocket 在建立连接之前关闭。”在生产?

在 AWS 上使用 ElastiCache 和 ElasticBeanstalk 配置 Redis

AWS中的高Redis延迟(ElastiCache)

AWS Elasticache - Redis VS MemcacheD

AWS Lambda:Redis ElastiCache 连接超时错误