Redis sentinel无法连接到主节点: 错误: READONLY 您不能在只读从节点上进行写操作。

7

我有一个使用sentinel体系结构的redis复制:

5个服务器: .1, .2, .3, .4.5

  • .1: nodejs应用程序, redis主节点, redis sentinel
  • .2: nodejs应用程序, redis从节点
  • .3: nodejs应用程序, redis从节点
  • .4: redis sentinel
  • .5: redis sentinel

Sentinel按预期处理故障转移。 redis-cli -h x.y.z.5 -p 26379 sentinel get-master-addr-by-name mymaster 总是返回 .1 服务器。

我连接到此复制的代码(我使用 ioredis):

let sentinels = [
  { host: process.env.REDIS_SENTINEL_1, port: process.env.REDIS_PORT },
  { host: process.env.REDIS_SENTINEL_2, port: process.env.REDIS_PORT },
  { host: process.env.REDIS_SENTINEL_3, port: process.env.REDIS_PORT }
]
store = new Redis({
  name: 'mymaster',
  sentinels: sentinels
})
store.on('ready', () => {
  store.set('foo', new Date())
  store.get('foo', function (err, result) {
    console.log('redis: get foo: ', result)
  });
});

然后,.1 的Node.js应用程序可以正常运行,但在.2.3中,出现了以下日志错误:

 events.js:161
       throw er; // Unhandled 'error' event
       ^

 Error: READONLY You can't write against a read only slave.

P/s: 我的Redis服务器绑定了服务器IP和127.0.0.1。因为,如果没有127.0.0.1,我会遇到这个错误:

Error: Redis connection to 127.0.0.1:6379 failed - connect ECONNREFUSED 127.0.0.1:6379

所以,我猜我的应用程序没有通过哨兵连接到Redis服务器,而是直接连接到本地主机(localhost)。

非常感谢任何帮助!

更新:当我打开DEBUG=ioredis:*时的日志:

2|MyApp     | Wed, 15 Mar 2017 12:58:42 GMT ioredis:redis status[x.y.z.5:26379]: connecting -> connect
2|MyApp     | Wed, 15 Mar 2017 12:58:42 GMT ioredis:redis status[x.y.z.5:26379]: connect -> ready
2|MyApp     | Wed, 15 Mar 2017 12:58:42 GMT ioredis:connection send 1 commands in offline queue
2|MyApp     | Wed, 15 Mar 2017 12:58:42 GMT ioredis:redis write command[0] -> sentinel(get-master-addr-by-name,mymaster)
2|MyApp     | events.js:161
2|MyApp     |       throw er; // Unhandled 'error' event
2|MyApp     |       ^
2|MyApp     | Error: READONLY You can't write against a read only slave.
2|MyApp     |     at JavascriptReplyParser._parseResult (/home/demo/demo_api/source/node_modules/redis/lib/parsers/javascript.js:43:16)
2|MyApp     |     at JavascriptReplyParser.try_parsing (/home/demo/demo_api/source/node_modules/redis/lib/parsers/javascript.js:114:21)
2|MyApp     |     at JavascriptReplyParser.run (/home/demo/demo_api/source/node_modules/redis/lib/parsers/javascript.js:126:22)
2|MyApp     |     at JavascriptReplyParser.execute (/home/demo/demo_api/source/node_modules/redis/lib/parsers/javascript.js:107:10)
2|MyApp     |     at Socket.<anonymous> (/home/demo/demo_api/source/node_modules/redis/index.js:131:27)
2|MyApp     |     at emitOne (events.js:96:13)
2|MyApp     |     at Socket.emit (events.js:189:7)
2|MyApp     |     at readableAddChunk (_stream_readable.js:176:18)
2|MyApp     |     at Socket.Readable.push (_stream_readable.js:134:10)
2|MyApp     |     at TCP.onread (net.js:551:20)
2|MyApp     | [nodemon] app crashed - waiting for file changes before starting...
2|MyApp     | sentinel production
2|MyApp     | Wed, 15 Mar 2017 12:58:43 GMT ioredis:redis status[localhost:6379]: [empty] -> connecting
2|MyApp     | Wed, 15 Mar 2017 12:58:43 GMT ioredis:redis status[x.y.z.5:26379]: [empty] -> connecting
2|MyApp     | Wed, 15 Mar 2017 12:58:43 GMT ioredis:redis queue command[0] -> sentinel(get-master-addr-by-name,mymaster)
2|MyApp     | sentinel production
2|MyApp     | Wed, 15 Mar 2017 12:58:43 GMT ioredis:redis status[localhost:6379]: [empty] -> connecting
2|MyApp     | Wed, 15 Mar 2017 12:58:43 GMT ioredis:redis status[x.y.z.5:26379]: [empty] -> connecting
2|MyApp     | Wed, 15 Mar 2017 12:58:43 GMT ioredis:redis queue command[0] -> sentinel(get-master-addr-by-name,mymaster)

更新2:还有一件事,我在本地机器上使用production环境运行该应用程序,一切都像魅力般工作了!
3个回答

12
如果出现上述错误,表示当前的Redis服务是只读的,没有写权限。估计该服务被用作从数据库。 解决方案1 打开对应Redis服务的配置文件,将属性slave-read-only的值修改为no,从而可以进行写操作。 解决方案2 或者更快的方法是通过运行以下命令:
redis-cli -h 127.0.0.1 -p 6379 slaveof no one

停止当前Redis服务接收来自其他Redis服务的同步命令,并将其升级到主数据库。

2

是的,终于找到了。

当旧代码使用redis模块并尝试直接连接到本地主机时,这是我的错误。

将其替换为ioredis实例,它就像魔术一样工作 :)


1
通过连接到“localhost”,.2和.3正在与其本地只读保存对话。 您不需要“通过sentinel连接”。您需要连接到sentinel,获取主IP,然后连接到该IP和端口。由于某种原因,您正在尝试连接到localhost,而不是sentinel报告的IP。至于为什么,我无法说,因为您的问题中没有提供信息。
Sentinel返回一个IP地址,而不是“localhost”,因此您没有使用来自sentinel的返回值,而是在某个地方编码为使用“localhost”,这将产生您报告的行为。您需要找到它并修复它。

谢谢您的评论,我也这么认为。但是在我的代码中,我已经尝试通过sentinel连接 - 就像我上面展示的那样。我不知道为什么它会尝试连接到本地主机? - Justin
更新2:还有一件事,我在我的本地机器上使用“生产”环境运行应用程序,一切都像魅力一样工作!!! - Justin
1
在我的情况下,问题是由于 replicaof localhost 6380 导致的。当将其替换为 replicaof 127.0.0.1 6380 时,问题得到解决。 - abbas

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