tokyo tyrant源码分析-主从复制实现

2022-06-22 00:00:00 文件 发送 请求 记录 循环

"tyrant分析-总体设计"中已经提到,slave起一个线程(do_slave)做主从复制,它和master建立tcp连接,发送请求命令和起始时间rts +1(上次的更新时间加1秒)给master,然后循环的从master那里接收一条条的记录,更新自己db、ulog和rts file。do_slave是以1秒为频率执行的。(实际是等待一次do_slave执行完毕后,再等待1秒,然后进入下一次的do_slave,依次循环。所以"以1秒为频率执行"的表达似乎并不准确。从下面可以看到一次do_slave有可能执行较长时间)



主从复制是一个主、从交互的过程。本节依次描述协议细节、slave细节、master细节。





------------

协议细节:



do_slave(slave) do_repl(master)

-------------------

| TTMAGICNUM|

| TTCMDREPL |

| ts (+1) |

| sid | send and recv (with timeout)

------------------- ------------------------>



-----------------

send and cnd wait | NOP |

<--------------------- -----------------





-----------------------

| TCULMAGICNUM |

| rts |

| rsid |

| rsiz |

content send | rsiz-content |

<--------------------- ------------------------



next content send

<---------------------



......



rsiz-content格式:

MAGIC + cmd + ksize + vsize + key + value

其中:

cmd: TTCMDPUT | TTCMDOUT | ...

ksize,vsize分别是本条记录的key,value的长度;

slave就根据cmd和key-value对对db进行相应操作。



master的ulog由一条条独立记录组成,每条记录有相同格式:

MAGIC + ts + sid + size + content

其中:

ts : 本条记录对应的时间戳。slave请求时会带上上次更新时间戳,master根据它们来判断需要传送哪些记录给slave;

sid : server id. 标识server。

size : 后面"content"长度

content格式即上面"rsiz-content"的格式,描述了一条key-value对以及对它做的操作命令。





--------------

do_slave流程:

打开rts文件(默认为ttserver.rts),读取上次的rts(replication timestamp);

和master建立socket连接(参数:-mhost,-mport),并设置socket选项:

SO_RCVTIMEO、SO_SNDTIMEO - 发送、接收超时设置为0.25秒

TCP_NODELAY - 禁止nagle算法

发送REPL请求(详见协议细节);

循环:

用recv接收数据;

解析接收数据,根据数据中指定的命令(TTCMDPUT、TTCMDOUT等)更新db和slave自己的ulog;

用接收数据里的新rts更新slave的rts文件;

后关闭连接



解释:

1、slave不能因偶然的网络故障之类永远阻塞在send或recv中,这样的话更新就会永远停滞了。所以它要设置发送和接收的超时。如果超时,则这次do_slave失败,等待1秒后进行下一次。send | recv失败时,它并不会用新的rts(可能压根就没请求到它)去更新自己的rts文件,所以下次还是会用旧的rts去请求,所以不会因do_slave失败而导致slave数据不全。

2、禁止nagle算法是因为有小数据的命令包的交互,不能拖延。

3、请求只发送一次,但数据是一直循环接收的。循环失败的条件是:recv失败(或超时),收到SIGINT或SIGTERM,或是更新库失败或写文件失败等;



---------------

do_repl流程:







根据slave的请求ts找到合适的ulog文件(文件名使用数字编号,依次递增),逻辑是:

从编号大的文件依次往编号小的文件:(编号越大,ulog内容越新,ts越大)

打开文件查看它的条记录的ts,如果请求ts大于它,则该文件即为要找的ulog文件。

循环。当对端连接未关闭且没收到SIGINT、SIGTERM信号时:

发送NOP(测试对端连接是否关闭);

pthread_cond_timedwait等待ulog更新信号,超时值为1秒;

循环:

一次读取一条日志记录;

加上头部(MAGIC,rts,rsid,rsiz。见"协议细节");

发送给slave。

当上面读取日志失败或发送失败时,退出循环。



解释:

1、ulog由一条条的记录组成,每条记录有相同格式: MAGIC + ts + sid + size + content

2、因为ulog文件有大小上限,所以写满一个后会写下一个。按上面所说那样,文件名用数字编号,依次递增;

3、找合适ulog文件的逻辑。因为是按内容从新到旧的顺序(也即ts从大到小的顺序)查看文件,所以先找到的其中条记录ts小于slave所请求ts的那个文件就是合适的文件;(该文件里ts会随着一条条记录慢慢增加,直到大于等于请求ts,这时就到了slave需要的数据处);

4、关于这两层循环的逻辑。内层循环一次发送一条记录,它是希望尽可能多地发送记录给slave,直到发送完所有记录(意外发送故障不考虑下)。退出到外层逻辑时希望这时又有ulog更新,能继续进行发送。这两层循环的目的都是希望能尽可能长地维持与slave的一次连接,从而让数据的同步更及时。
————————————————
版权声明:本文为CSDN博主「pingnning」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/pingnning/article/details/4724377

相关文章