【redis源码学习】从源码角度看主从复制(2):主从之间的“三次握手”

void syncWithMaster(connection *conn) {

char tmpfile[256], *err = NULL;

int dfd = -1, maxtries = 5;

int psync_result;

/* If this event fired after the user turned the instance into a master

  • with SLAVEOF NO ONE we must just return ASAP. */

if (server.repl_state == REPL_STATE_NONE) {

connClose(conn);

return;

}

/* Check for errors in the socket: after a non blocking connect() we

  • may find that the socket is in error state. */

if (connGetState(conn) != CONN_STATE_CONNECTED) {

serverLog(LL_WARNING,“Error condition on socket for SYNC: %s”,

connGetLastError(conn));

goto error;

}

//根据 server.repl_state 执行对应操作,具体见下文

/* Send a PING to check the master is able to reply without errors. */

if (server.repl_state == REPL_STATE_CONNECTING) {

serverLog(LL_NOTICE,“Non blocking connect for SYNC fired the event.”);

/* Delete the writable event so that the readable event remains

  • registered and we can wait for the PONG reply. */

connSetReadHandler(conn, syncWithMaster);

connSetWriteHandler(conn, NULL);

server.repl_state = REPL_STATE_RECEIVE_PONG;

/* Send the PING, don’t check for errors at all, we have the timeout

  • that will take care about this. */

err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,“PING”,NULL);

if (err) goto write_error;

return;

}

/* Receive the PONG command. */

if (server.repl_state == REPL_STATE_RECEIVE_PONG) {

err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);

/* We accept only two replies as valid, a positive +PONG reply

  • (we just check for “+”) or an authentication error.

  • Note that older versions of Redis replied with "operation not

  • permitted" instead of using a proper error code, so we test

  • both. */

if (err[0] != ‘+’ &&

strncmp(err,“-NOAUTH”,7) != 0 &&

strncmp(err,“-ERR operation not permitted”,28) != 0)

{

serverLog(LL_WARNING,“Error reply to PING from master: ‘%s’”,err);

sdsfree(err);

goto error;

} else {

serverLog(LL_NOTICE,

“Master replied to PING, replication can continue…”);

}

sdsfree(err);

server.repl_state = REPL_STATE_SEND_AUTH;

}

/* AUTH with the master if required. */

if (server.repl_state == REPL_STATE_SEND_AUTH) {

if (server.masteruser && server.masterauth) {

err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,“AUTH”,

server.masteruser,server.masterauth,NULL);

if (err) goto write_error;

server.repl_state = REPL_STATE_RECEIVE_AUTH;

return;

} else if (server.masterauth) {

err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,“AUTH”,server.masterauth,NULL);

if (err) goto write_error;

server.repl_state = REPL_STATE_RECEIVE_AUTH;

return;

} else {

server.repl_state = REPL_STATE_SEND_PORT;

}

}

/* Receive AUTH reply. */

if (server.repl_state == REPL_STATE_RECEIVE_AUTH) {

err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);

if (err[0] == ‘-’) {

serverLog(LL_WARNING,“Unable to AUTH to MASTER: %s”,err);

sdsfree(err);

goto error;

}

sdsfree(err);

server.repl_state = REPL_STATE_SEND_PORT;

}

/* Set the slave port, so that Master’s INFO command can list the

  • slave listening port correctly. */

if (server.repl_state == REPL_STATE_SEND_PORT) {

int port;

if (server.slave_announce_port) port = server.slave_announce_port;

else if (server.tls_replication && server.tls_port) port = server.tls_port;

else port = server.port;

sds portstr = sdsfromlonglong(port);

err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,“REPLCONF”,

“listening-port”,portstr, NULL);

sdsfree(portstr);

if (err) goto write_error;

sdsfree(err);

server.repl_state = REPL_STATE_RECEIVE_PORT;

return;

}

/* Receive REPLCONF listening-port reply. */

if (server.repl_state == REPL_STATE_RECEIVE_PORT) {

err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);

/* Ignore the error if any, not all the Redis versions support

  • REPLCONF listening-port. */

if (err[0] == ‘-’) {

serverLog(LL_NOTICE,"(Non critical) Master does not understand "

“REPLCONF listening-port: %s”, err);

}

sdsfree(err);

server.repl_state = REPL_STATE_SEND_IP;

}

/* Skip REPLCONF ip-address if there is no slave-announce-ip option set. */

if (server.repl_state == REPL_STATE_SEND_IP &&

server.slave_announce_ip == NULL)

{

server.repl_state = REPL_STATE_SEND_CAPA;

}

/* Set the slave ip, so that Master’s INFO command can list the

  • slave IP address port correctly in case of port forwarding or NAT. */

if (server.repl_state == REPL_STATE_SEND_IP) {

err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,“REPLCONF”,

“ip-address”,server.slave_announce_ip, NULL);

if (err) goto write_error;

sdsfree(err);

server.repl_state = REPL_STATE_RECEIVE_IP;

return;

}

/* Receive REPLCONF ip-address reply. */

if (server.repl_state == REPL_STATE_RECEIVE_IP) {

err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);

/* Ignore the error if any, not all the Redis versions support

  • REPLCONF listening-port. */

if (err[0] == ‘-’) {

serverLog(LL_NOTICE,"(Non critical) Master does not understand "

“REPLCONF ip-address: %s”, err);

}

sdsfree(err);

server.repl_state = REPL_STATE_SEND_CAPA;

}

/* Inform the master of our (slave) capabilities.

  • EOF: supports EOF-style RDB transfer for diskless replication.

  • PSYNC2: supports PSYNC v2, so understands +CONTINUE .

  • The master will ignore capabilities it does not understand. */

if (server.repl_state == REPL_STATE_SEND_CAPA) {

err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,“REPLCONF”,

“capa”,“eof”,“capa”,“psync2”,NULL);

if (err) goto write_error;

sdsfree(err);

server.repl_state = REPL_STATE_RECEIVE_CAPA;

return;

}

/* Receive CAPA reply. */

if (server.repl_state == REPL_STATE_RECEIVE_CAPA) {

err = sendSynchronousCommand(SYNC_CMD_READ,conn,NULL);

/* Ignore the error if any, not all the Redis versions support

  • REPLCONF capa. */

if (err[0] == ‘-’) {

serverLog(LL_NOTICE,"(Non critical) Master does not understand "

“REPLCONF capa: %s”, err);

}

sdsfree(err);

server.repl_state = REPL_STATE_SEND_PSYNC;

}

/* Try a partial resynchonization. If we don’t have a cached master

  • slaveTryPartialResynchronization() will at least try to use PSYNC

  • to start a full resynchronization so that we get the master run id

  • and the global offset, to try a partial resync at the next

  • reconnection attempt. */

if (server.repl_state == REPL_STATE_SEND_PSYNC) {

if (slaveTryPartialResynchronization(conn,0) == PSYNC_WRITE_ERROR) {

err = sdsnew(“Write error sending the PSYNC command.”);

goto write_error;

}

server.repl_state = REPL_STATE_RECEIVE_PSYNC;

return;

}

//执行到这里,主从握手阶段已经完成。

/* If reached this point, we should be in REPL_STATE_RECEIVE_PSYNC. */

if (server.repl_state != REPL_STATE_RECEIVE_PSYNC) {

serverLog(LL_WARNING,"syncWithMaster(): state machine error, "

“state should be RECEIVE_PSYNC but is %d”,

server.repl_state);

goto error;

}

psync_result = slaveTryPartialResynchronization(conn,1);

if (psync_result == PSYNC_WAIT_REPLY) return; /* Try again later… */

/* If the master is in an transient error, we should try to PSYNC

  • from scratch later, so go to the error path. This happens when

  • the server is loading the dataset or is not connected with its

  • master and so forth. */

if (psync_result == PSYNC_TRY_LATER) goto error;

/* Note: if PSYNC does not return WAIT_REPLY, it will take care of

  • uninstalling the read handler from the file descriptor. */

if (psync_result == PSYNC_CONTINUE) {

serverLog(LL_NOTICE, “MASTER <-> REPLICA sync: Master accepted a Partial Resynchronization.”);

if (server.supervised_mode == SUPERVISED_SYSTEMD) {

redisCommunicateSystemd(“STATUS=MASTER <-> REPLICA sync: Partial Resynchronization accepted. Ready to accept connections.\n”);

redisCommunicateSystemd(“READY=1\n”);

}

return;

}

/* PSYNC failed or is not supported: we want our slaves to resync with us

  • as well, if we have any sub-slaves. The master may transfer us an

  • entirely different data set and we have no way to incrementally feed

  • our slaves after that. */

disconnectSlaves(); /* Force our slaves to resync with us as well. */

freeReplicationBacklog(); /* Don’t allow our chained slaves to PSYNC. */

/* Fall back to SYNC if needed. Otherwise psync_result == PSYNC_FULLRESYNC

  • and the server.master_replid and master_initial_offset are

  • already populated. */

if (psync_result == PSYNC_NOT_SUPPORTED) {

serverLog(LL_NOTICE,“Retrying with SYNC…”);

if (connSyncWrite(conn,“SYNC\r\n”,6,server.repl_syncio_timeout*1000) == -1) {

serverLog(LL_WARNING,“I/O error writing to MASTER: %s”,

strerror(errno));

goto error;

}

}

/* Prepare a suitable temp file for bulk transfer */

if (!useDisklessLoad()) {

while(maxtries–) {

snprintf(tmpfile,256,

“temp-%d.%ld.rdb”,(int)server.unixtime,(long int)getpid());

dfd = open(tmpfile,O_CREAT|O_WRONLY|O_EXCL,0644);

if (dfd != -1) break;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

这个月马上就又要过去了,还在找工作的小伙伴要做好准备了,小编整理了大厂java程序员面试涉及到的绝大部分面试题及答案,希望能帮助到大家

在这里插入图片描述

在这里插入图片描述

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

这个月马上就又要过去了,还在找工作的小伙伴要做好准备了,小编整理了大厂java程序员面试涉及到的绝大部分面试题及答案,希望能帮助到大家

[外链图片转存中…(img-BJfa22ev-1712707241642)]

[外链图片转存中…(img-7jdVbFt4-1712707241642)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 26
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值