您的位置:首页 > 数据库 > Redis

redis主从复制过程中,slave端混读PING命令的问题

2016-01-08 12:43 781 查看
最近两日在调试redis代码时遇到两个问题:

1.主从复制过程中,slave端的readSyncBulkPayload会读到错误的信息(主服务器发来的PING命令的一部分),导致slave端一直在向主服务器发送同步请求。

2.主从同步结束后,进入命令传播阶段。主服务器向从服务器传播命令后,如果命令执行成功,从服务器会返回+OK\r\n,主服务器不能识别这个命令,导致失败。

经过两天的调试后发现问题的发生原因如下:

1.如下图:



主服务器在发送PING时,从服务器会有一段时间处于readSyncBulkPayload事件处理阶段,如果这时读到了PING就会出错。

而redis源码是这样避免这个问题的:

int prepareClientToWrite(redisClient *c)
{
if ((c->flags & REDIS_MASTER) && !(c->flags & REDIS_MASTER_FORCE_REPLY)) return REDIS_ERR;

if (c->fd <= 0) return REDIS_ERR;

if (c->bufpos == 0 && listLength(c->reply) == 0 &&
(c->replstate == REDIS_REPL_NONE ||c->replstate == REDIS_REPL_ONLINE) &&
aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,sendReplyToClient, c) == AE_ERR) return REDIS_ERR;
return REDIS_OK;
}
可以看到它在这才判断client的状态,如果他的状态处于NONE或者ONLINE才会绑定写事件。而它在ONLINE时已完成了同步,进入readQueryFromClient函数处理的阶段了。

redis在这里设计的思想是,slave如果处于END之后的阶段,主服务器的命令对它来说就是新的命令了,而当它处于ONLINE阶段时,才能正式的从主服务器读取命令。

2.从服务器向主服务器发送+PONG时,进入perpareClientToWrite()。结果发现它是主服务器而又没有REDIS_MASTER_FORCE_REPLY,所以不会向主服务器发送PONG。

而之所以从服务器向主服务器发送ACK会成功,原因如下:

void replicationSendAck(void) {
redisClient *c = server.master;

int optVal = 0;
int optLen = sizeof(optVal);
getsockopt(c->fd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optLen);
if (c != NULL) {
c->flags |= REDIS_MASTER_FORCE_REPLY;
addReplyMultiBulkLen(c, 3);
addReplyBulkCString(c, "REPLCONF");
addReplyBulkCString(c, "ACK");
// 发送偏移量
addReplyBulkLongLong(c, c->reploff);
c->flags &= ~REDIS_MASTER_FORCE_REPLY;
}
}


因为设置了 REDIS_MASTER_FORCE_REPLY,就会成功绑定sendReplyToClient
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: