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

来源:互联网 发布:今创集团 怎么样知乎 编辑:程序博客网 时间:2024/05/25 08:14

 

 

"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的一次连接,从而让数据的同步更及时。

 

原创粉丝点击