前面我们提到过,现在Redis进行复制,从服务器是使用了Psync命令代替了Sync命令,下面介绍一下Psync命令的实现
Psync的调用方式有两种
根据情况的不同,接收到Psync命令的主服务器会向从服务器返回以下三种回复之一
如果主服务器返回+FULLRESYNC 回复给从服务器,则代表主服务器将要进行完整重同步,runid是主服务器的运行ID,从服务器接受到这个运行ID之后就会保存起来,在下一次需要发送Psync命令时试用,而offset就是主服务器当前的复制偏移量,从服务器此时会将这个值作为自己的复制偏移量初值,然后进行维护起来(前面提到过,主从服务器都会维护自己的一个复制偏移量),此时从服务器需要等待主服务器发送RDB文件,载入后,再等待主服务器发送缓冲池在空白期执行的写命令,然后执行这些写命令,至此完成同步。
如果主服务器返回的是+CONTINUE回复,那么表示主服务器将与从服务器执行部分重同步操作,从服务器只要等着主服务器将自己缺少的那部分数据发送过来就可以了(主服务器从复制积压缓冲池中取出)
如果主服务器返回-ERR回复,表示版本不匹配,主服务器的版本太低(低于2.8),无法识别从服务器发送的Psync命令,但并不会停止同步,而是从服务器会向主服务器发送Sync命令,完成同步操作
过程如下图所示
上面介绍了Psync命令的过程,但没有具体到其如何进行同步和复制的实现
整个复制同步过程总共有7个步骤
当客户端向从服务器发送一下命令时:
slaveof 127.0.0.1 6379
首先要做的就是将客户端给定的主服务器IP地址以及端口号保存到服务器状态的masterhost属性和masterport属性里面
Stcuct redisServer{ //... 前面的一些信息 //主服务器地址 char *masterhost; //主服务器端口号 int masterport; //.... };
SLAVEOF命令也是一个异步命令,会启用另一个进程去执行,在完成保存主服务器IP地址和端口号的工作后,从服务器将发送OK回复给客户端
这一步只是代表复制指令被接收,下面才会真正去执行复制工作
得到了主服务器IP地址和端口号,下面的一步肯定是要进行主从服务器之间连接
从服务器会自己新建一个套接字,然后使用这个套接字连接到服务器,如果连接成功,收到连接成功回复的从服务器套接字会自动去绑定专门用于处理复制工作的文件事件处理器,这个处理器将会负责执行后续的复制工作,比如去接收RDB文件、接收后面主服务器通过传播发送的写命令
而主服务器在接受从服务器套接字连接之后,会为该套接字创建相应的一个客户端状态,将从服务器视作一个客户端去对待,那么此时从服务器就可以向主服务器发送命令请求,而主服务器可以接受从服务器发送的命令请求进行返回命令回复(不熟悉套接字连接可以回看前面系列的套接字连接)
完成连接之后,从服务器就成为了主服务器的一个客户端,从服务器第一件事就是去发送Ping命令,一般客户端向服务器发送Ping命令,服务器会返回Pong的信息
发送ping命令主要有两个作用
从服务器在发送Ping命令之后将遇到下列三种情况之一
第一种是网络状态不佳,主服务器接收到了Ping已经回复了pong,但从服务器不可以在规定时限内读取出命令回复的内容,这样就表示主从服务器之间的网络连接状态不佳,不可以继续执行复制工作的后续步骤,当出现这种情况时,从服务器会断开连接,然后重新创建连接主服务器的套接字。
第二种是主服务出错,主服务器会向从服务器返回一个错误,表示主服务器暂时没办法处理从服务器的命令请求,不能够执行复制工作的后续步骤,当出现这种情况时,从服务器也是会断开连接,然后重新创建连接主服务器的套接字。
第三种是正常情况,从服务器读取到了Pong回复,表示主从服务器之间连接状态正常并且主服务器可以正常相应从服务器的请求,然后会继续执行下面的复制工作
现在从服务器可以接收主服务器返回的PONG,证明连接一切正常,下一步就是要进行身份验证了
那么对于主服务器这边就会出现4种情况
在进行身份验证后,从服务器将执行下列的命令
REPLCONF listening-port <port-number>
其实就是从服务器将自己的监听端口号发送给主服务器,主服务器在接收到这个命令之后,就会将该监听端口号保存在主服务器上的客户端状态,对应的属性为slave_listening_port属性中
typedef struct redisClient( //。。。 //从服务器的监听端口号 int slave_listening_port; //.. )redisClient;
这个属性目前并没有什么作用,目前唯一的作用就是主服务器接收客户端的info repliication命令时得到的回复中会显示,下图中对应的port属性就是了(可能是主服务器那边需要记录自己的从服务器的信息)
来到了这里,从服务器在主服务器中的客户端信息已经完整,并且主从服务器之间连接没有问题,就可以进行同步操作了。
这里要注意的是,在同步步骤之前,前面的所有步骤,从服务器都是主服务器的客户端,但来到了这一步,从服务器不仅是主服务器的客户端,同时,主服务器也是从服务器的客户端,这是因为后面的缓冲区里面的写命令是由主服务器发送给从服务器的,此时只要主服务器为从服务器的客户端,那么直接发送写命令给从服务器去执行即可。
上一文章已经说过部分重同步和完整重同步的实现,这里就不再赘述
因此,在同步操作执行之后,主从服务器双方都是对方的客户端,他们可以互相向对方发送命令请求,互相向对方发送命令结果返回。
当完成了同步之后,此时主从数据库的状态是一致的,但为了让之后也可以保持一致,就需要进行命令传播,这时主服务器只要一直将自己执行的写命令发送给从服务器(主从服务器已经可以互相请求了),而从服务器只要一直接收并且执行主服务器发送来的写命令,就可以保证主从服务器状态一致了。