Redis源码分析(十九)--- replication主从数据复制的实现 Redis源码分析(十九)--- replication主从数据复制的实现

Redis源码分析(十九)--- replication主从数据复制的实现

              replication的英文单词的原意是“复制”的意思,replication文件作为我在Data目录下的分析的最后一个文件,足以说明他的重要性,代码量1800+,的确非常难啃。只能说个我看代码下来的大致印象吧,要我画个结构图好好理理这里面各个API的关系图,这个我目前还真做不到。说到主从复制,这个是实现读写分离的最好手段了,也很常见,当用户数达到一定量,当一个服务器承受不了达到上千万的pv时,采取主从数据库的形式也是一般架构师能够想到的一种手段。Redis的主从数据库在我这里就称为主客户端,从客户端,因为客户端中有所属于的db,因为数据库基于客户单本身进行复制操作的。也就是说,一个Redis,存在一个master主客户端,多个slave从客户端,到时实现的就是slave向主客户端进行复制操作。因为API比较多,进行了稍稍的归类:

  1. /* ---------------------------------- MASTER -------------------------------- */  
  2. void createReplicationBacklog(void/* 创建backlog的buffer */  
  3. void resizeReplicationBacklog(long long newsize) /* 调整复制备份日志的大小,当replication backlog被修改的时候 */  
  4. void freeReplicationBacklog(void/* 释放备份日志 */  
  5. void feedReplicationBacklog(void *ptr, size_t len) /* 往备份日志中添加添加数据操作,会引起master_repl_offset偏移量的增加 */  
  6. void feedReplicationBacklogWithObject(robj *o) /* 往backlog添加数据,以Redis 字符串对象作为参数 */  
  7. void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) /* 将主数据库复制到从数据库 */  
  8. void replicationFeedMonitors(redisClient *c, list *monitors, int dictid, robj **argv, int argc) /* 发送数据给monitor监听者客户端 */  
  9. long long addReplyReplicationBacklog(redisClient *c, long long offset) /* slave从客户单添加备份日志 */  
  10. int masterTryPartialResynchronization(redisClient *c) /* 主数据库尝试分区同步 */  
  11. void syncCommand(redisClient *c) /* 同步命令函数 */  
  12. void replconfCommand(redisClient *c) /* 此函数用于从客户端进行配置复制进程中的执行参数设置 */  
  13. void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) /* 给slave客户端发送BULK数据 */  
  14. void updateSlavesWaitingBgsave(int bgsaveerr) /* 此方法将用于后台保存进程快结束时调用,更新slave从客户端 */  
  15.       
  16. /* ----------------------------------- SLAVE -------------------------------- */  
  17. void replicationAbortSyncTransfer(void/* 中止与master主数据的同步操作 */  
  18. void replicationSendNewlineToMaster(void/* 从客户端发送空行给主客户端,破坏了原本的协议格式,避免让主客户端检测出从客户端超时的情况 */  
  19. void replicationEmptyDbCallback(void *privdata) /* 清空数据库后的回调方法,当老数据被刷新出去之后等待加载新数据的时候调用 */  
  20. void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) /* 从客户端读取同步的Sync的BULK数据 */  
  21. char *sendSynchronousCommand(int fd, ...) /* 从客户端发送给主客户端同步数据的命令,附上验证信息,和一些参数配置信息 */  
  22. int slaveTryPartialResynchronization(int fd) /* 从客户端尝试分区同步操作 */  
  23. void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) /* 与主客户端保持同步,期间包括端口号等的确认,socket连接 */  
  24. int connectWithMaster(void/* 连接主客户端 */  
  25. void undoConnectWithMaster(void/* 撤销连接主客户端 */  
  26. int cancelReplicationHandshake(void/* 当已经存在一个复制进程时,中止一个非阻塞的replication复制的尝试 */  
  27. void replicationSetMaster(char *ip, int port) /* 设定主客户端的ip地址和端口号 */  
  28. void replicationUnsetMaster(void)  
  29. void slaveofCommand(redisClient *c)  
  30. void roleCommand(redisClient *c)  
  31. void replicationSendAck(void/* 发送ACK包给主客户端 ,告知当前的进程偏移量 */  
  32.       
  33. /* ---------------------- MASTER CACHING FOR PSYNC -------------------------- */  
  34. void replicationCacheMaster(redisClient *c) /* 缓存客户端信息 */  
  35. void replicationDiscardCachedMaster(void/* 当某个客户端将不会再回复的时候,可以释放掉缓存的主客户端 */  
  36. void replicationResurrectCachedMaster(int newfd) /* 将缓存客户端复活 */  
  37.       
  38. /* ------------------------- MIN-SLAVES-TO-WRITE  --------------------------- */  
  39. void refreshGoodSlavesCount(void/* 更新slave从客户端数量 */  
  40. void replicationScriptCacheInit(void)  
  41. void replicationScriptCacheFlush(void)  
  42. void replicationScriptCacheAdd(sds sha1)  
  43. int replicationScriptCacheExists(sds sha1)  
  44. void replicationCron(void)  
找一个标准的slave从客户端向主客户端实现同步的操作:

  1. /* 与主客户端保持同步,期间包括端口号等的确认,socket连接 */  
  2. void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {  
  3.     char tmpfile[256], *err;  
  4.     int dfd, maxtries = 5;  
  5.     int sockerr = 0, psync_result;  
  6.     socklen_t errlen = sizeof(sockerr);  
  7.     REDIS_NOTUSED(el);  
  8.     REDIS_NOTUSED(privdata);  
  9.     REDIS_NOTUSED(mask);  
  10.   
  11.     /* If this event fired after the user turned the instance into a master 
  12.      * with SLAVEOF NO ONE we must just return ASAP. */  
  13.     if (server.repl_state == REDIS_REPL_NONE) {  
  14.         close(fd);  
  15.         return;  
  16.     }  
  17.   
  18.     /* Check for errors in the socket. */  
  19.     /* socket连接是否正常 */  
  20.     if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &errlen) == -1)  
  21.         sockerr = errno;  
  22.     if (sockerr) {  
  23.         aeDeleteFileEvent(server.el,fd,AE_READABLE|AE_WRITABLE);  
  24.         redisLog(REDIS_WARNING,"Error condition on socket for SYNC: %s",  
  25.             strerror(sockerr));  
  26.         goto error;  
  27.     }  
  28.   
  29.     /* If we were connecting, it's time to send a non blocking PING, we want to 
  30.      * make sure the master is able to reply before going into the actual 
  31.      * replication process where we have long timeouts in the order of 
  32.      * seconds (in the meantime the slave would block). */  
  33.     /* 连接测试,将由主客户端发送PING命令给从客户端,在给定的延迟时间内观察是否有回复 */  
  34.     if (server.repl_state == REDIS_REPL_CONNECTING) {  
  35.         redisLog(REDIS_NOTICE,"Non blocking connect for SYNC fired the event.");  
  36.         /* Delete the writable event so that the readable event remains 
  37.          * registered and we can wait for the PONG reply. */  
  38.         aeDeleteFileEvent(server.el,fd,AE_WRITABLE);  
  39.         server.repl_state = REDIS_REPL_RECEIVE_PONG;  
  40.         /* Send the PING, don't check for errors at all, we have the timeout 
  41.          * that will take care about this. */  
  42.         //发送PING命令  
  43.         syncWrite(fd,"PING\r\n",6,100);  
  44.         return;  
  45.     }  
  46.   
  47.     /* Receive the PONG command. */  
  48.     //收到回复了  
  49.     if (server.repl_state == REDIS_REPL_RECEIVE_PONG) {  
  50.         char buf[1024];  
  51.   
  52.         /* Delete the readable event, we no longer need it now that there is 
  53.          * the PING reply to read. */  
  54.         aeDeleteFileEvent(server.el,fd,AE_READABLE);  
  55.   
  56.         /* Read the reply with explicit timeout. */  
  57.         buf[0] = '\0';  
  58.         if (syncReadLine(fd,buf,sizeof(buf),  
  59.             server.repl_syncio_timeout*1000) == -1)  
  60.         {  
  61.             redisLog(REDIS_WARNING,  
  62.                 "I/O error reading PING reply from master: %s",  
  63.                 strerror(errno));  
  64.             goto error;  
  65.         }  
  66.   
  67.         /* We accept only two replies as valid, a positive +PONG reply 
  68.          * (we just check for "+") or an authentication error. 
  69.          * Note that older versions of Redis replied with "operation not 
  70.          * permitted" instead of using a proper error code, so we test 
  71.          * both. */  
  72.         if (buf[0] != '+' &&  
  73.             strncmp(buf,"-NOAUTH",7) != 0 &&  
  74.             strncmp(buf,"-ERR operation not permitted",28) != 0)  
  75.         {  
  76.             redisLog(REDIS_WARNING,"Error reply to PING from master: '%s'",buf);  
  77.             goto error;  
  78.         } else {  
  79.             redisLog(REDIS_NOTICE,  
  80.                 "Master replied to PING, replication can continue...");  
  81.         }  
  82.     }  
  83.   
  84.     /* AUTH with the master if required. */  
  85.     //auth身份验证  
  86.     if(server.masterauth) {  
  87.         err = sendSynchronousCommand(fd,"AUTH",server.masterauth,NULL);  
  88.         if (err[0] == '-') {  
  89.             redisLog(REDIS_WARNING,"Unable to AUTH to MASTER: %s",err);  
  90.             sdsfree(err);  
  91.             goto error;  
  92.         }  
  93.         sdsfree(err);  
  94.     }  
  95.   
  96.     /* Set the slave port, so that Master's INFO command can list the 
  97.      * slave listening port correctly. */  
  98.     /* 设置从客户端监听端口 */  
  99.     {  
  100.         sds port = sdsfromlonglong(server.port);  
  101.         err = sendSynchronousCommand(fd,"REPLCONF","listening-port",port,  
  102.                                          NULL);  
  103.         sdsfree(port);  
  104.         /* Ignore the error if any, not all the Redis versions support 
  105.          * REPLCONF listening-port. */  
  106.         if (err[0] == '-') {  
  107.             redisLog(REDIS_NOTICE,"(Non critical) Master does not understand REPLCONF listening-port: %s", err);  
  108.         }  
  109.         sdsfree(err);  
  110.     }  
  111.   
  112.     /* Try a partial resynchonization. If we don't have a cached master 
  113.      * slaveTryPartialResynchronization() will at least try to use PSYNC 
  114.      * to start a full resynchronization so that we get the master run id 
  115.      * and the global offset, to try a partial resync at the next 
  116.      * reconnection attempt. */  
  117.     psync_result = slaveTryPartialResynchronization(fd);  
  118.     if (psync_result == PSYNC_CONTINUE) {  
  119.         redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Master accepted a Partial Resynchronization.");  
  120.         return;  
  121.     }  
  122.   
  123.     /* Fall back to SYNC if needed. Otherwise psync_result == PSYNC_FULLRESYNC 
  124.      * and the server.repl_master_runid and repl_master_initial_offset are 
  125.      * already populated. */  
  126.     if (psync_result == PSYNC_NOT_SUPPORTED) {  
  127.         redisLog(REDIS_NOTICE,"Retrying with SYNC...");  
  128.         if (syncWrite(fd,"SYNC\r\n",6,server.repl_syncio_timeout*1000) == -1) {  
  129.             redisLog(REDIS_WARNING,"I/O error writing to MASTER: %s",  
  130.                 strerror(errno));  
  131.             goto error;  
  132.         }  
  133.     }  
  134.   
  135.     /* Prepare a suitable temp file for bulk transfer */  
  136.     while(maxtries--) {  
  137.         snprintf(tmpfile,256,  
  138.             "temp-%d.%ld.rdb",(int)server.unixtime,(long int)getpid());  
  139.         dfd = open(tmpfile,O_CREAT|O_WRONLY|O_EXCL,0644);  
  140.         if (dfd != -1) break;  
  141.         sleep(1);  
  142.     }  
  143.     if (dfd == -1) {  
  144.         redisLog(REDIS_WARNING,"Opening the temp file needed for MASTER <-> SLAVE synchronization: %s",strerror(errno));  
  145.         goto error;  
  146.     }  
  147.   
  148.     /* Setup the non blocking download of the bulk file. */  
  149.     if (aeCreateFileEvent(server.el,fd, AE_READABLE,readSyncBulkPayload,NULL)  
  150.             == AE_ERR)  
  151.     {  
  152.         redisLog(REDIS_WARNING,  
  153.             "Can't create readable event for SYNC: %s (fd=%d)",  
  154.             strerror(errno),fd);  
  155.         goto error;  
  156.     }  
  157.   
  158.     server.repl_state = REDIS_REPL_TRANSFER;  
  159.     server.repl_transfer_size = -1;  
  160.     server.repl_transfer_read = 0;  
  161.     server.repl_transfer_last_fsync_off = 0;  
  162.     server.repl_transfer_fd = dfd;  
  163.     server.repl_transfer_lastio = server.unixtime;  
  164.     server.repl_transfer_tmpfile = zstrdup(tmpfile);  
  165.     return;  
  166.   
  167. error:  
  168.     close(fd);  
  169.     server.repl_transfer_s = -1;  
  170.     server.repl_state = REDIS_REPL_CONNECT;  
  171.     return;  
  172. }  
         在replication中,要一个cacheMaster的概念,就是可以临时缓存主客户端的信息,一般用于突然master和slave断开连接的时候,可以下次进行主从同步的时候快速恢复:

  1. /* 缓存客户端信息 */  
  2. void replicationCacheMaster(redisClient *c) {  
  3.     listNode *ln;  
  4.   
  5.     redisAssert(server.master != NULL && server.cached_master == NULL);  
  6.     redisLog(REDIS_NOTICE,"Caching the disconnected master state.");  
  7.   
  8.     /* Remove from the list of clients, we don't want this client to be 
  9.      * listed by CLIENT LIST or processed in any way by batch operations. */  
  10.     //首先移除此客户端  
  11.     ln = listSearchKey(server.clients,c);  
  12.     redisAssert(ln != NULL);  
  13.     listDelNode(server.clients,ln);  
  14.   
  15.     /* Save the master. Server.master will be set to null later by 
  16.      * replicationHandleMasterDisconnection(). */  
  17.     //保存为缓存客户端  
  18.     server.cached_master = server.master;  
  19.   
  20.     /* Remove the event handlers and close the socket. We'll later reuse 
  21.      * the socket of the new connection with the master during PSYNC. */  
  22.     //删除在这个客户端上的读写事件  
  23.     aeDeleteFileEvent(server.el,c->fd,AE_READABLE);  
  24.     aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);  
  25.     close(c->fd);  
  26.   
  27.     /* Set fd to -1 so that we can safely call freeClient(c) later. */  
  28.     c->fd = -1;  
  29.   
  30.     /* Invalidate the Peer ID cache. */  
  31.     if (c->peerid) {  
  32.         sdsfree(c->peerid);  
  33.         c->peerid = NULL;  
  34.     }  
  35.   
  36.     /* Caching the master happens instead of the actual freeClient() call, 
  37.      * so make sure to adjust the replication state. This function will 
  38.      * also set server.master to NULL. */  
  39.     replicationHandleMasterDisconnection();  
  40. }  
当想让这个master的复活的时候,调用下面的方法:

  1. /* Turn the cached master into the current master, using the file descriptor 
  2.  * passed as argument as the socket for the new master. 
  3.  * 
  4.  * This funciton is called when successfully setup a partial resynchronization 
  5.  * so the stream of data that we'll receive will start from were this 
  6.  * master left. */  
  7. /* 将缓存客户端复活 */  
  8. void replicationResurrectCachedMaster(int newfd) {  
  9.     //将cached_master赋值为主客户端  
  10.     server.master = server.cached_master;  
  11.     server.cached_master = NULL;  
  12.     server.master->fd = newfd;  
  13.     server.master->flags &= ~(REDIS_CLOSE_AFTER_REPLY|REDIS_CLOSE_ASAP);  
  14.     server.master->authenticated = 1;  
  15.     server.master->lastinteraction = server.unixtime;  
  16.     server.repl_state = REDIS_REPL_CONNECTED;  
  17.   
  18.     /* Re-add to the list of clients. */  
  19.     //重新添加入客户端列表中  
  20.     listAddNodeTail(server.clients,server.master);  
  21.     if (aeCreateFileEvent(server.el, newfd, AE_READABLE,  
  22.                           readQueryFromClient, server.master)) {  
  23.         redisLog(REDIS_WARNING,"Error resurrecting the cached master, impossible to add the readable handler: %s", strerror(errno));  
  24.         freeClientAsync(server.master); /* Close ASAP. */  
  25.     }  
  26.   
  27.     /* We may also need to install the write handler as well if there is 
  28.      * pending data in the write buffers. */  
  29.     if (server.master->bufpos || listLength(server.master->reply)) {  
  30.         if (aeCreateFileEvent(server.el, newfd, AE_WRITABLE,  
  31.                           sendReplyToClient, server.master)) {  
  32.             redisLog(REDIS_WARNING,"Error resurrecting the cached master, impossible to add the writable handler: %s", strerror(errno));  
  33.             freeClientAsync(server.master); /* Close ASAP. */  
  34.         }  
  35.     }  
  36. }  
当然如果确定在未来不糊在使用缓存的master的时,可以彻底摧毁:

  1. /* Free a cached master, called when there are no longer the conditions for 
  2.  * a partial resync on reconnection. */  
  3. /* 当某个客户端将不会再回复的时候,可以释放掉缓存的主客户端 */  
  4. void replicationDiscardCachedMaster(void) {  
  5.     if (server.cached_master == NULL) return;  
  6.   
  7.     redisLog(REDIS_NOTICE,"Discarding previously cached master state.");  
  8.     server.cached_master->flags &= ~REDIS_MASTER;  
  9.     //直接释放客户端  
  10.     freeClient(server.cached_master);  
  11.     //server的缓存客户端赋值为NULL  
  12.     server.cached_master = NULL;  
  13. }  
在这里面靠的就是server.cached_master属性。slave在和master连接的时候,要进行master的ip地址和Port端口的确认:

  1. /* Set replication to the specified master address and port. */  
  2. /* 设定主客户端的ip地址和端口号 */  
  3. void replicationSetMaster(char *ip, int port) {  
  4.     sdsfree(server.masterhost);  
  5.     server.masterhost = sdsdup(ip);  
  6.     server.masterport = port;  
  7.     //设置完毕之后,断开所有的连接,中止replication进程  
  8.     if (server.master) freeClient(server.master);  
  9.     disconnectSlaves(); /* Force our slaves to resync with us as well. */  
  10.     replicationDiscardCachedMaster(); /* Don't try a PSYNC. */  
  11.     freeReplicationBacklog(); /* Don't allow our chained slaves to PSYNC. */  
  12.     cancelReplicationHandshake();  
  13.     server.repl_state = REDIS_REPL_CONNECT;  
  14.     server.master_repl_offset = 0;  

猜你喜欢

转载自blog.csdn.net/tuxedolinux/article/details/80314120
今日推荐