1、 redis的主从数据同步源码分析
在(11)中我们分析了主从服务器间的数据同步机制,这里我们接着从源码的角度来继续分析其数据同步。
主从服务器的数据同步,我们依然从最基础的slaveof命令开始分析。首先假设我们有两台服务器A与B,我们要用A作为主服务器,B作为从服务器。这时只需要用客户端连接到B,然后输入slaveof加上A的ip与端口便可。
B在收到命令后,会按正常的命令执行流程来执行该命令,如同之前的命令一样,可以在server.c中可以找该命令与其对应的方法,如下图:
可以看见与slaveof命令对应的是一个叫replicaofCommand的方法,这个方法实现在replication.c文件中,其实现内容如下:
void replicaofCommand(client *c) {
/* SLAVEOF is not allowed in cluster mode as replication is automatically
* configured using the current address of the master node. */
if (server.cluster_enabled) {
addReplyError(c,"REPLICAOF not allowed in cluster mode.");
return;
}
/* The special host/port combination "NO" "ONE" turns the instance
* into a master. Otherwise the new master address is set. */
if (!strcasecmp(c->argv[1]->ptr,"no") &&
!strcasecmp(c->argv[2]->ptr,"one")) {
if (server.masterhost) {
replicationUnsetMaster();
sds client = catClientInfoString(sdsempty(),c);
serverLog(LL_NOTICE,"MASTER MODE enabled (user request from '%s')",
client);
sdsfree(client);
}
} else {
long port;
if (c->flags & CLIENT_SLAVE)
{
/* If a client is already a replica they cannot run this command,
* because it involves flushing all replicas (including this
* client) */
addReplyError(c, "Command is not valid when client is a replica.");
return;
}
if ((getLongFromObjectOrReply(c, c->argv[2], &port, NULL) != C_OK))
return;
/* Check if we are already attached to the specified slave */
if (server.masterhost && !strcasecmp(server.masterhost,c->argv[1]->ptr)
&& server.masterport == port) {
serverLog(LL_NOTICE,"REPLICAOF would result into synchronization with the master we are already connected with. No operation performed.");
addReplySds(c,sdsnew("+OK Already connected to specified master\r\n"));
return;
}
/* There was no previous master or the user specified a different one,
* we can continue. */
replicationSetMaster(c->argv[1]->ptr, port);
sds client = catClientInfoString(sdsempty(),c);
serverLog(LL_NOTICE,"REPLICAOF %s:%d enabled (user request from '%s')",
server.masterhost, server.masterport, client);
sdsfree(client);
}
addReply(c,shared.ok);
}
这个方法的重点在第11行,这是一个if else语句,当slaveof命令后面添加的参数是no one的时候,if条件成立,表示从服务器将不在作为从服务器同步指定的主服务器。这个在哨兵模式下,哨兵发现主服务器故障后,更换主服务器的时候会用到。
然后是else语句,这里有一大堆if语句检查服务器状态,最重要的是在第44行,执行了一个replicationSetMaster方法,这个方法将传入的ip与端口的redis设置为主服务器。其具体内容如下:
/* Set replication to the specified master address and port. */
void replicationSetMaster(char *ip, int port) {
int was_master = server.masterhost == NULL;
sdsfree(server.masterhost);
server.masterhost = sdsnew(ip);
server.masterport = port;
if (server.master) {
freeClient(server.master);
}
disconnectAllBlockedClients(); /* Clients blocked in master, now slave. */
/* Force our slaves to resync with us as well. They may hopefully be able
* to partially resync with us, but we can notify the replid change. */
disconnectSlaves();
cancelReplicationHandshake();
/* Before destroying our master state, create a cached master using
* our own parameters, to later PSYNC with the new master. */
if (was_master) {
replicationDiscardCachedMaster();
replicationCacheMasterUsingMyself();
}
server.repl_state = REPL_STATE_CONNECT;
}
这段代码最重要的地方在第6行、第7行和第23行,这几处为几个重要的参数进行了赋值。第6、7行记录了主服务的ip与端口,第23行标识了从服务器下一阶段需要去连接主服务器。