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

最近两日在调试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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值