回复错误:连接到 Redis 集群 AWS 后出现 MOVED 错误

8

我正在构建一个连接到Redis的Node.js应用程序。我已经使用本地Redis实例完成了这个工作。现在,我正在使用ioredis从我的Node.js应用程序连接到AWS中的k8s Redis集群。以下是我的代码。

const Redis = require("ioredis");

this.redis = new Redis({
 port: 6379, // Redis port
 host: rhost, // Redis host
 password: password
});

this.redis.on('connect', () => {
 console.log('connected to redis')
})

看起来我已经成功连接到我的集群,因为日志中打印了消息connected to redis。然而,每次我尝试使用我的redis对象时,都会出现MOVED错误:

UnhandledPromiseRejectionWarning: ReplyError: MOVED 5011 <ip address>:6379
  at parseError (/node_modules/ioredis/node_modules/redis-parser/lib/parser.js:179:12)
  at parseType (/node_modules/ioredis/node_modules/redis-parser/lib/parser.js:302:14)

这个连接在我的本地可以工作。但是在 AWS 上不行。我试过使用 Redis.Cluster 对象替换 Redis,但是在部署应用程序后,应用程序会挂起并且连接事件永远不会触发。 closereconnecting 事件似乎无限循环。

据我理解,这可能是集群节点之间重定向的问题。也许是主/从配置的问题。AWS 中的错误是否是配置问题?我需要使用 Redis.Cluster 对象而不是普通的 Redis 实例吗?修复 MOVED 错误的最佳方法是什么?


1
如果你因为"redis-cli"出现了类似的错误而来到这里,那么你必须使用它的"-c"参数来处理集群重定向。 - Alexander Farber
5个回答

15
MOVED错误是由于直接使用Redis客户端和ElastiCache(Redis集群模式)的配置端点引起的。可以通过使用new Redis.Cluster([{ host: <cfg-endpoint>])来解决这个问题,它知道多个分片。
否则禁用Redis集群模式并使用您的ElastiCache集群的主(主节点)DNS名称。即使没有Redis集群模式,仍然有故障转移策略(AWS在主节点失败时会替换主节点),并且您可以将节点部署到多个可用区域中。
此外,当启用加密时,您需要启用TLS连接AWS Elasticache(ioredis的选项为tls: {})。 https://aws.amazon.com/blogs/database/work-with-cluster-mode-on-amazon-elasticache-for-redis/

3
您可以在此处了解有关MOVE错误的更多信息: https://redis.io/topics/cluster-spec#moved-redirection “MOVED重定向 Redis客户端可以向集群中的每个节点发送查询,包括从节点。节点将分析查询,如果它是可接受的(即,查询中只提到一个键,或提到的多个键都属于同一哈希槽),则会查找负责键或键所属哈希槽的节点。
如果哈希槽由节点服务,则简单地处理查询,否则节点将检查其内部哈希槽到节点映射,并向客户端回复一个MOVED错误,例如以下示例:
GET x -MOVED 3999 127.0.0.1:6381
错误包括键的哈希槽(3999)和可以处理查询的实例的ip:port。客户端需要重新发出查询到指定节点的IP地址和端口。请注意,即使客户端在重新发出查询之前等待很长时间,并且在此期间群集配置发生了更改,目标节点如果哈希槽3999现在由另一个节点服务,仍将再次回复MOVED错误。如果联系的节点没有更新的信息,则情况也是如此。”
ioredis支持redis集群。因此,您应该像这样创建redis集群:
new Redis.Cluster([{
    host: process.env.REDIS_ENDPOINT, // Configuration endpoint address from Elasticache
    port: process.env.REDIS_PORT
  }]);

ioredis将处理MOVE重定向错误。


3
const Redis = require("ioredis");
const connection = await new Redis.Cluster(
        [redisHost,redisPort],
        {
      scaleReads: "all",
      redisOptions: {
        username: redisUserName,
        password: redisPassword,
        enableAutoPipelining: true,
      }
      );

上述配置解决了我的问题,我现在使用ioredis代替'redis'。


你如何配置本地客户端呢?在本地测试时,你是否运行了 Redis 集群模式? - Dmase05
1
我在本地使用以下配置:const connection = await new Redis(),它会自动连接到我的本地redis 127.0.0.1:6379。 - Arya Mohanan

1
我也遇到了同样的问题,因为我使用了与Redis集群模式不兼容的new Redis()实例。
为了使其与Redis集群兼容,请尝试以下配置:
//import section
  import { Cluster, Redis } from 'ioredis';

// connection config
   const connection = new Cluster([{
          host: process.env.host,
          port: parseInt(process.env.port),
        }], {
          slotsRefreshTimeout: 2000,
          // dnsLookup: This is needed when the addresses of startup nodes are hostnames instead of IPs.
          dnsLookup: (address, callback) => callback(null, address),
          redisOptions: {
            username: process.env.username,
            password: process.env.password,
            tls: {},
          }
    });

以上解决方案经过尝试和验证!
我正在使用最新版本(5.3.2)的ioredis包。

0

如果这不起作用:

const Redis = require('ioredis');

const ioCluster = new Redis.Cluster([redisConfig.redis]);
const slackQueue = new Queue('slack notifications', {
  prefix: '{slack}' ,
  createClient: () => ioCluster
});
const emailQueue = new Queue('email notifications', {
  prefix: '{email}' ,
  createClient: () => ioCluster
});

我会放弃使用 ioredis 或尝试降级 Redis 引擎到 4.x


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接