我正在构建一个连接到 Redis 的 Nodejs 应用程序。我将此与我的本地 redis 实例一起使用。现在,我使用
ioredis
从我的 Nodejs 应用程序连接到 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
进行交换,但在部署应用程序后,应用程序挂起并且连接事件永远不会触发。 close
和 reconnecting
事件似乎无限循环。
根据我的理解,这是集群中节点之间重定向的问题。可能是主/从配置的问题。该错误是 AWS 中的配置问题吗?我是否需要使用
Redis.Cluster
对象而不是普通的 Redis
实例?修复 MOVED 错误的最佳方法是什么?
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/
您可以在此处阅读有关 MOVE 错误的更多信息: https://redis.io/topics/cluster-spec#moved-redirection “移动重定向 Redis 客户端可以自由地将查询发送到集群中的每个节点,包括从节点。节点会分析查询,如果可以接受(即查询中只提到了一个键,或者提到的多个键都属于同一个哈希槽),它将查找哪个节点负责该哈希槽钥匙所属的地方。
如果哈希槽由节点提供,则简单地处理查询,否则节点将检查其内部哈希槽到节点映射,并向客户端回复 MOVED 错误,如下例所示:
获取x -已移动 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 重定向错误。
const Redis = require("ioredis");
const connection = await new Redis.Cluster(
[redisHost,redisPort],
{
scaleReads: "all",
redisOptions: {
username: redisUserName,
password: redisPassword,
enableAutoPipelining: true,
}
);
上述配置解决了我的问题,我正在使用 ioredis 代替“redis”。
如果这没有帮助:
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
我也遇到了同样的问题,因为我使用了与 Redis 集群模式不兼容的
new Redis()
实例。
我正在使用最新版本的 [ioredis][1]
包(5.3.2
)。
为了兼容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: {},
}
});
以上解决方案已经过尝试和测试!