我们部署在线上的fake slave(用于同步数据或者是采集数据供业务订阅)经常会遇到某些特殊情况下,比如网络异常导致无法收到对端fin包导致无法走正常的连接关闭流程,这就导致了该slave连接处于假死状态。
之前实现binlog协议解析的时候,由于发送了dump命令后,该连接基本就处于只读模式,假设连接在此时异常,那么要么依赖tcp底层的keepalive,该连接基本就处于僵死状态了,会导致新的数据无法被fake slave获取到。
今天主要是把异常自恢复的场景先实现了,一个是主挂了之后自动切到从机来继续拉binlog数据,还有一个就是异常连接的检测了。在业务上,该场景最简单也最直接的解决办法就是加入业务层的心跳包,于是大概搜了下MySQL的document,查了下heartbeat的关键字,发现真有这个特性,是以前自己忽略了,该特性在MySQL 5.5被添加。
实现方式非常简单,直接使用以下语句打开心跳,心跳只有在binlog被全部读完后,一段时间间隔下无新数据后才会被发送,用于让从来知道master是否还活着:
SET @master_heartbeat_period = <interval>
interval的但是是纳秒。
对应的binlog事件也非常简单,大概格式为:
Header
Timestamp 4
EventType 1
ServerID 4
EventSize 4
LogPos 4
Flags 2
Payload
LogIdent eof string
最开头就是19字节的binlog头,剩下的payload是一个eof string,也就是剩余的字节组成的字符串,一般就是当前binlog的文件名。
slave处理心跳也很简单,假设在读包堵塞了Heartbeat interval的时间后,断开连接然后重连即可,当然我这儿是设置了1.5倍的时间。
该binlog结构对应的结构体为:
class Heartbeat_event: public Binary_log_event
{
public:
/**
Sent by a master to a slave to let the slave know that the master is
still alive. Events of this type do not appear in the binary or relay logs.
They are generated on a master server by the thread that dumps events and
sent straight to the slave without ever being written to the binary log.
@param buf Contains the serialized event.
@param event_len Length of the serialized event.
@param description_event An FDE event, used to get the
following information
-binlog_version
-server_version
-post_header_len
-common_header_len
The content of this object
depends on the binlog-version currently in use.
*/
Heartbeat_event(const char* buf, unsigned int event_len,
const Format_description_event *description_event);
const char* get_log_ident() { return log_ident; }
unsigned int get_ident_len() { return ident_len; }
protected:
const char* log_ident;
unsigned int ident_len; /** filename length */
};