XCOMMySQL Group Replication中的一个分布式通讯引擎,它是基于Paxos协议研发的,是Group Replication最底层的一个关键组件。它的主要作用:

  1. 消息的顺序一致性分发:确保所有事物(消息)在各节点分发顺序一致
  2. 组视图管理: 管理group内的成员节点变更
  3. 故障探测:定时探测故障节点

官方架构图如下:

XCOM是一个Paxos变体,与raft有很大相同,实现还是很复杂,主要是用C语言写的(代码目录:plugin/group_replication/libmysqlgcs/)。这里详细解释一下其故障探测机制。

xcom 引擎的入口函数是xcom_base.c中的xcom_thread_main , 该函数调用了xcom_thread_init();初始化了各种基础变量以及task线程,这里初始化的东西还是很多,许多都是paxos相关的元素,其中存活探测线程也在其中。

存活探测的具体实现是在文件xcom_detector.c 中,在xcom_base.c中对其进行了extern声明。xcom_detector.c中的函数alive_task是存活探测的主题函数。

在头文件:plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/xcom_detector.h中定义了变量:

1
#define DETECTOR_LIVE_TIMEOUT 5.0

它是一个写死的宏,定义了一个节点存活失效时间是5s。但在alive_task函数中并没有用到这个宏。
alive_task函数调用了may_be_dead函数。
may_be_dead 函数判断一个节点上次探测时间如果是4s以前则认为这个节点是suspicion, 其调用函数alive_task就会对其ping,以确认该节点是否还存活。

1
2
3
4
5
/* See if node has been suspiciously still for some time */
int may_be_dead(detector_state const ds, node_no i, double seconds) {
/* DBGOUT(FN; NDBG(i,u); NDBG(ds[i] < seconds - 2.0, d)); */
return ds[i] < seconds - 4.0;
}

代码中return ds[i] < seconds - 4.0;数字4也是写死了,并不是一个可配置的变量。

这段代码判断一个节点是否成为suspicion的条件就是看它的上一次探测成功的时间是不是5s以前。每次探测成功都会把这个值改为当前时间. 它定义在site_def中 detector_state detected;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct site_def {
synode_no start; /* Config is active from this message number */
synode_no boot_key; /* The message number of the original unified_boot */
node_no nodeno; /* Node number of this node */
node_list nodes; /* Set of nodes in this config */
server *servers[NSERVERS]; /* Connections to other nodes */
detector_state detected; /* Time of last incoming message for each node */
node_no global_node_count; /* Number of live nodes in global_node_set */
node_set global_node_set; /* The global view */
node_set local_node_set; /* The local view */
int detector_updated; /* Has detector state been updated? */
xcom_proto x_proto;
synode_no delivered_msg[NSERVERS];
double install_time;
};

alive_task 函数在xcom engine运行期间是一个死循环,一直做存活探测。它做两方面探测,一是告诉group中的其它成员自己还活着,二是对其它成员是否存活做探测。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/* Send alive messages periodically */
int alive_task(task_arg arg MY_ATTRIBUTE((unused))) {
DECL_ENV
pax_msg *i_p;
pax_msg *you_p;
END_ENV;
TASK_BEGIN

ep->i_p = ep->you_p = NULL;

while (!xcom_shutdown) {
double sec = task_now();
synode_no alive_synode = get_current_message();
site_def const *site = find_site_def(alive_synode);

/*
If a new configuration has been forced, the site's configuration may be
invalid. Specifically, this function is called to verify if the site's
node number is valid and fix it if this is not valid.
*/
validate_update_configuration(site, alive_synode);

if (site && get_nodeno(site) != VOID_NODE_NO) {
/* Send alive if we have not been active for some time */
if (server_active(site, get_nodeno(site)) < sec - 0.5) {
replace_pax_msg(&ep->i_p, pax_msg_new(alive_synode, site));
ep->i_p->op = i_am_alive_op;
send_to_all_site(site, ep->i_p, "alive_task");
}

/* Ping nodes which seem absent */
{
node_no i;
for (i = 0; i < get_maxnodes(site); i++) {
if (i != get_nodeno(site) && may_be_dead(site->detected, i, sec)) {
replace_pax_msg(&ep->you_p, pax_msg_new(alive_synode, site));
ep->you_p->op = are_you_alive_op;

ep->you_p->a = new_app_data();
ep->you_p->a->app_key.group_id = ep->you_p->a->group_id =
get_group_id(site);
ep->you_p->a->body.c_t = xcom_boot_type;
init_node_list(1, &site->nodes.node_list_val[i],
&ep->you_p->a->body.app_u_u.nodes);

DBGOUT(FN; COPY_AND_FREE_GOUT(
dbg_list(&ep->you_p->a->body.app_u_u.nodes)););
send_server_msg(site, i, ep->you_p);
}
}
}
}
TASK_DELAY(1.0);
}
FINALLY
replace_pax_msg(&ep->i_p, NULL);
replace_pax_msg(&ep->you_p, NULL);
TASK_END;
}

while循环体中TASK_DELAY(1.0);确保每循环一次暂停1秒。执行逻辑如下:

  1. 首先更新视图(validate_update_configuration),使自己的视图和group中最新视图一致
  2. 判断自己否还存活(server_active),如果自己alive,则对group中其它成员进行广播
  3. ping group中的其它成员,但这一步并不一定对每个成员都执行,仅对may_be_dead函数return true的成员执行(前面那个条件i != get_nodeno(site)是过滤掉自己)。may_be_deadreturn true ,说明该节点上次ping成功是5s以前了,该再ping一次了。因此实际上xcom是至少5s中对其它成员做一次ping探测。

<<END

Hey! I’m open to a new position. If your company is hiring, welcome to contact me! (weixin: cq_mount) Thanks!

参考文档:

https://mysqlhighavailability.com/the-king-is-dead-long-live-the-king-our-homegrown-paxos-based-consensus/