50

VDL:唯品会强一致、高可用、高性能分布式日志存储 (质量篇)

 6 years ago
source link: http://mp.weixin.qq.com/s/ItvhgOX_6cZ0hL9vNr_zFA
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

VDL:唯品会强一致、高可用、高性能分布式日志存储 (质量篇)

Original 陈非 唯技术 2017-12-27 11:02 Posted on

这是VDL系列的第三篇,本文主要讲述VDL在质量控制上的一些工作。 

FIU测试

Image分布式系统,在正常情况下还是比较简单的。异常情况才是分布式系统的难点,包括节点故障、磁盘异常、网络延时/丢包、网络分区等等。如何在这些异常或者异常组合的情况下保证VDL稳定正常,这是VDL的重难点之一。

FIU是Fault Injection Utility的缩写,是我们为了更好地测试分布式系统(RDP/VDL)自研的一个小工具。FIU的主要目的是:自动化构造各种异常/错误,并且具备定制扩展的能力。

VDL使用FIU来模拟上述各种异常,并测试在异常情况下,VDL是否正常(表现是否符合预期)。我们先来简单介绍一下FIU框架。

FIU框架

如上图所示,FIU由三个组件组成:

-fiu_driver:driver是总控节点,是所有test case的发起端,为每一个test case准备所需的异常/错误等前提条件,然后执行设计的test case; fiu_driver和fiu_agent之间通过UDP协议通讯;

-fiu_agent:和测试的目标进程运行在相同机器上,负责接受fiu_driver的指令并在目标测试的机器上进行各种操作,完成这些操作一般通过编写shell脚本完成;FIU本身提供一些shell原型,用户开发的shell原型会自动同步到fiu_driver端,所以测试行为可以灵活扩展;fiu_agent和fiu_engine之间通过named shared memory同步fiu_driver开启的sync point;

-fiu_engine:以源代码和so两种方式嵌入到被测试的项目中,主要是插入各种sync point 和各种定点异常。

目前VDL的异常用例有30多例,还在不断地添加中,主要分类有: 

1.模拟磁盘IO异常:包括磁盘写入异常、写入延时;

2.模拟网络异常:包括丢包,Delay,使用TC进行模拟。我们并不处理拜占庭的情况; 

3.进程异常:包括随机Kill/Kill -9 VDL节点; 

4.Raft流程异常:包括Delete Raft Log,落后节点加入集群,不断停止Leader节点等情况;

5.Log Store异常:包括数据损坏、索引文件异常等情况;

6.客户端异常测试:包括客户端边界请求、异常请求测试。

FIU的例子:

我们举一个例子方便与更加直观理解。假设我们有一个三节点组成的VDL集群,配合FIU,部署图如下:

(FIU Driver也可以跟agent机器部署在一起)

上图中fiu_driver用于给agent发送执行请求,异常测试用例脚本会部署在driver所在服务器,在执行用例过程中,脚本会调用fiu_driver给agent发送命令。fiu_driver不是常驻进程。

fiu_agent接收fiu_driver的请求,执行操作。主要有执行shell脚本与往Share Mem Map 注入异常信息。fiu_agent是常驻进程。

假设我们要完成如下的异常测试:

1.#####################################

3.# 流程:

5.# 测试与Raft流程相关的存储接口异常时,VDL集群情况

6.# 1) 启动3节点集群,启动发送服务

7.# 2) 选取一个节点,往存储接口注入错误

8.# 3) 判断节点情况

9.# 4) 恢复节点,清除错误

10.# 5) 循环2-4,向下一个接口注入错误

11.# 6) 共测试20次

12.# 7) 完成20次异常后,清除错误,关闭发送服务,并检查

14.# 预期:

15.# 1) 注入错误后,节点会crash

16.# 2) 集群恢复正常后, 集群有Leader, 且三节点raft log数据一致

18.# ###################################

那么需要实现一个测试用例,并放在driver所在的机器上:

1.function TestCase_logstore_error() {

2.// 过程先略过

我们按用例的流程介绍:

-第一步: 启动3节点集群,启动发送服务

1.function TestCase_logstore_error() { 

3....

4.# 启动vdl0

5.local start_vdl0=`${fiu_driver} ${vdl0_ip} ${fiu_port} 'remote.sync.pipe' 'sh '${vdl_root}'/tools/fiu/fiu_agent/script/start_vdl.sh' '10000'`

6.# 检查vdl0是否正确

7.Expect_EQ $start_vdl0 "1"

9.# 启动vdl1

10.local start_vdl1=`${fiu_driver} ${vdl1_ip} ${fiu_port} 'remote.sync.pipe' 'sh '${vdl_root}'/tools/fiu/fiu_agent/script/start_vdl.sh' '10000'`

11.# 检查vdl1是否正确

12.Expect_EQ $start_vdl1 "1"

14.# 启动vdl2

15....

17.# 启动发送服务

18.# 启动发送服务会执行本地的一个shell脚本,包括动态编译producer并启动,不再详细展开。

19.echo '-------------produce message async(start)----------------'

20.${vdl_root}/tools/fiu/fiu_agent/script/pmsg.sh 6000 5 t

脚本中,start_vdl1命令中的fiu_driver是driver的路径,vdl0_ip是要启动的VDL的IP地址,fiu_port是agent的port,remote.sync.pipe是指要同步执行, xxxx/start_vdl.sh是指agent要执行的脚本,10000是指超时时间。

启动VDL,测试脚本会调用driver,往agent发送启动命令。agent接受到请求后,执行本地脚本并返回结果,流程图如下:

-第二步: 选取一个节点,往存储接口注入错误

1.function TestCase_logstore_error() {

3.......

5. #这里定义要注入的错误的数组

6.local store_interface_error_array=('fiu_logstore_entries_error' 'fiu_logstore_term_error' ...省略)

7.local error_array_count=${#store_interface_error_array[@]}

9.#------节点注入错误--------------------------------

10.for((i=0;i<20;i++))

12.# 伪随机选取要注入的异常

13.local inject_node=$((i%3))

14.local inject_error_index=$(( i % error_array_count ))

16.echo '--------注入错误-----------'

17.PrepareOneCondition ${vdl_ip_array[inject_node]} ${fiu_port}'sync.point'${store_interface_error_array[inject_error_index]}

19.#下面是检查代码

20....

关键代码是:

1.PrepareOneCondition ${vdl_ip_array[inject_node]} ${fiu_port}'sync.point'${store_interface_error_array[inject_error_index]}

PrepareOneCondition是共用函数,实际是调用driver,往agent执行'sync.point'命令。流程如下:

往Share Mem Map插入Key后,VDL使用fiu_engine的接口,就能获取到对应的错误信号,代码如下:

1. // 下面是Go代码

2.func (s *LogStore) Term(i uint64) (uint64, error) {

4.// mock error

5.if fiu.IsSyncPointExist(vdlfiu.FiuStoreTermError) {

6.return 0, errors.New("Mock FiuStoreTermError")

-后面几步: 与前面的第一、二步类似,例如"判断节点情况",则Driver发送'remote.sync.pipe'请求到agent,agent执行"检查VDL脚本",返回结果。

到目前为止,我们介绍了VDL如何使用异常测试对各种情况进行模拟。下面我们来讲讲如何校验节点间数据一致性。

节点数据一致性校验

Image

判断VDL是否能正常服务,可以简单通过监控页面看到,但如何才能简单地确认集群的数据是一致的呢?VDL在压测环境下,三个副本,目前共生产2400多亿条数据,并且还在不断增长中,灰度环境两天就能产生15亿条数据,如何才能简单校验副本间数据都是一致的呢?

由于数据量太多,不可能对每条数据进行对比,且每个节点的log文件不相同,也不可以简单使用md5直接对比文件。对此我们自研了一个数据校验工具,校验节点间的数据一致性。使用的方案是连续checksum的方式对数据进行对比。

如图所示,三个节点均从某个index开始,计算连续的checksum,只要相同的index有相同的checksum,则认为三节点数据均一致。

在实际使用中,我们将每个节点的checksum数据按一定规则输出到日志中,我们只要校验最新的相同的index,若有相同的checksum,则在["计算的index", "校验的index"] 这个区间的数据均一致。

推荐阅读

VDL:唯品会强一致、高可用、高性能分布式日志存储介绍(产品篇)

VDL:唯品会强一致、高可用、高性能分布式日志存储介绍(实现篇)

Image

唯品会大数据平台优化


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK