分布式系统设计和测试总结

分布式系统设计和测试总结
序⾔:在当今,有⼀门技术很是热门,那就是分布式技术,也许很多⼈对分布式技术很疑惑,但是实际上,你总是与分布式技术打交道,我们若想更好的把握住以后的机遇,那么则要让⾃⼰不断的对这些系统基础知识了解,这样才能去创新。有不对的地⽅请指教,谢谢啦
  ⼀、分布式系统和编程
  1、分布式计算:将⼀个⼤型的⾼难度的计算拆分成若⼲个⼦进程进⾏计算,然后计算完毕后,回收结果。这个需要区分的是并⾏计算,并⾏计算是指并发执⾏,分为空间和时间的并⾏,空间则是我们常说的多核,⽽时间则是利⽤了流⽔线,错开时间。他们的共同点是解决对象上,都将⼤任务化为⼩任务,这是他们共同之处。区别在于前者的每个任务具有独⽴性,并且更关注的是任务间的通信。后者的任务包是⼀个划分,之间的联系很⼤。
  2、分布式:⼀个分布式系统是若⼲个计算机操作系统组成,但是对于⽤户⽽⾔,就像⼀个巨型计算机⼀样。
  3、分布式编程:简单⽽⾔,,其主要特征是分布和通信。即将⼀个⼤型软件系统,分割成若⼲个模块,然后模块之间利⽤规范好的接⼝进⾏通信。
  例如:我常⽤的是的分布式编程,包括:RMI、Corba以及SOAP
  RMI是java虚拟机模块之间的通信。
  Corba是通过IDL描述接⼝,不同编程语⾔模块之间可通过Cobra服务进⾏通信。
  SOAP是靠XML来描述接⼝,通过HTTP协议进⾏通信。
单角钢
  4、分布式存储:将⼀个⼤型计算机资源可以分配到不同的存储系统上,整体可以看做⼀个⼤型存储系统。例如:现在很⽕的Hadoop的HDFS 就提供了这么⼀个功能。
  所以,总之,分布式重点就在于:能够统⼀管理和分配资源,协调好各个分布式模块之间的通信。
  ⼆、分布式应⽤
  举⼏个简单的例⼦说说分布式
  1、我们常⽤的也是分布式的,浏览器和服务器之间的交互是通过http协议,⽽可以将各个资源分配到各个服务器,⽽我们常说的就是这么来的。
  2、在⼀些C/S系统中,也利⽤了分布式,将计算和资源进⾏分配,互相之间利⽤接⼝进⾏通信。
  三、分布式测试和应⽤
  常见的测试范围
  1、模块测试;对分布式系统中的单个模块进⾏测试。因为分布式模块中的模块有⼀定独⽴性,所以先保证每个模块的功能。这部分可以考虑⽤来实现⼀部分测试。
  2、接⼝测试;因为分布式系统之间的通信主要应⽤接⼝,所以专门针对接⼝进⾏测试。这⽅⾯可以考虑⽤通过⾃动化进⾏请求和相应实现⼀部分测试。
  3、;全⾯将各个模块进⾏部署,按照功能需求进⾏测试。这部分的话,可以依靠⼀些上层的,例如:WEB的话可以是ui级别的⾃动化测试。
  4、性能压⼒测试;⾼压⼒和⾼并发下测试,各个模块之间的响应速度和接收程度等。例如:web系统的⾼并发测试,C/S系统的多个客户端同时请求服务器。
  5、模拟测试;即尽可能真实模拟应⽤环境。如果系统太过庞⼤,也可计算⽐例进⾏模拟。
  四、分布式⾃动化测试平台
  1、测试资源可以分配到各个执⾏端进⾏测试。
  2、测试结果可以统⼀收取集中到服务器上进⾏查看。
  3、可以多个服务器保存测试资源。
  4、可以监控和跟踪⾃动化测试过程
  总之,即测试执⾏资源可以进⾏分配、测试数据可以分配、测试结果可以分配。但是对于整个外界⽽⾔却是⼀个整体系统。
  总结:不同的软件系统有不同的测试⽅法,我们如果要对⼀个系统能够充分进⾏测试,⼀定要了解其运作原理、然后从原理出发,合理划分其测试类型和测试需求,根据测试类型和测试需求然后到具体的测试⽅法,这样才能⼀步⼀步提⾼我们的测试效率。不能做其然⽽不知其所以然吧。
>>>>>>>>
本话题系列⽂章整理⾃ PingCAP NewSQL Meetup 第 26 期刘奇分享的《深度探索分布式系统测试》议题现场实录。⽂章较长,为⽅
便⼤家阅读,会分为上中下三篇,本⽂为上篇。
今天主要是介绍分布式系统测试。对于 PingCAP ⽬前的现状来说,我们是觉得做好分布式系统测试⽐做⼀个分布式系统更难。就是你把它写出来不是最难的,把它测好才是最难的。⼤家肯定会觉得有这么夸张吗?那我们先从⼀个最简单的、每个⼈都会写的 Hello world 开始。
A simple “Hello world” is a miracle
We should walk through all of the bugs in:
Compiler
Linker
VM (maybe)
OS
其实这个 Hello world 能够每次都正确运⾏已经是⼀个奇迹了,为什么呢?⾸先,编译器得没 bug,链接器得没 bug ;然后我们可能跑在 VM 上,那 VM 还得没 bug;并且 Hello world 那还有⼀个 syscall,那我们还得保证操作系统没有 bug;到这还不算吧,我们还得要硬件没有 bug。所以⼀个最简单程序它能正常运⾏起来,我们要穿越巨长的⼀条路径,然后这个路径⾥⾯所有的东西都不能出问题,我们才能看到⼀个最简单的 Hello world。
但是分布式系统⾥⾯呢,就更加复杂了。⽐如⼤家现在⽤的很典型的微服务。假设你提供了⼀个微服务,然后在微服务提供的功能就是输出⼀个Hello world ,然后让别⼈来 Call。
A RPC “Hello world” is a miracle
We should walk through all of the bugs in:
Coordinator (zookeeper, etcd)
RPC implementation
Network stack
Encoding/Decoding library
Compiler for programming languages or [protocol buffers, avro, msgpack, capn]
那么我们可以看⼀下它的路径。我们起码需要依赖 Coordinator 去做这种服务发现,⽐如⽤ zookeeper,etcd ,⼤家会感觉是这东西应该很稳定了吧?但⼤家可以去查⼀下他们每⼀次 release notes,⾥边说我们 fix 了哪些 bug,就是所有⼤家印象中⾮常稳定的这些东西,⼀直都在升级,每⼀
次升级都会有 bug fix。但换个思路来看,其实我们也很幸运,因为⼤部分时候我们没有碰到那个 bug,然后 RPC 的这个实现不能有问题。当然如果⼤家深度使⽤ RPC,⽐如说 gRPC,你会发现其实 bug 还是挺多的,⽤的深⼀点,基本上就会发现它有 bug。还有系统⽹络协议栈,去年 TCP 被爆出有⼀个 checksum 问题,就是 Linux 的 TCP 协议栈,这都是印象中永远不会出问题的。再有,编解码,⼤家如果有 Go 的经验的话,可以看⼀下 Go 的 JSON 历史上从发布以来更新的记录,也会发现⼀些 bug。还有更多的⼤家喜欢的编解码,⽐如说你⽤ Protocol buffers、Avro、Msgpack、Cap’n 等等,那它们本⾝还需要 compiler 去⽣成⼀个代码,然后我们还需要那个 compiler ⽣成的代码是没有 bug 的。然后这⼀整套下来,我们这个程序差不多是能运⾏的,当然我们没有考虑硬件本⾝的 bug。
其实⼀个正确的运⾏程序从概率上来讲(不考虑宇宙射线什么的这种),已经是⾮常幸运的了。当然每⼀个系统都不是完善的,那通常情况下,为什么我们这个就运⾏的顺利呢?因为我们的测试永远都测到了正确的路径,我们跑⼀个简单的测试⼀定是把正确的路径测到了,但是这中间有很多错误路径其实我们都没有碰到。然后我不知道⼤家有没有印象,如果写 Go 程序的时候,错误处理通常写成 if err != nil,然后 return error ,不知道⼤家写了多少。那其它程序、其它的语⾔⾥就是 try.catch,然后⾥⾯各种 error 处理。就是⼀个真正完善的系统,最终的错误处理代码实际上通常会⽐你写正常逻辑代码还要多的,但是我们的测试通常 cover 的是正确的逻辑,就是实际上我们测试的 cover 是⼀⼩部分。
那先纠正⼏个观念,关于测试的。就是到底怎么样才能得到⼀个好的、⾼质量的程序,或者说得到⼀个⾼质量的系统?
Who is the tester ?
Quality comes from solid engineering.
Stop talking and go build things.
Don’t hire too many testers.
Testing is owned by the entire team. It is a culture, not a process.
Are testers software engineers? Yes.
Hiring good people is the first step. And then keep them challenged.
我们的观念是说先有 solid engineering 。我觉得这个⼏乎是勿庸置疑的吧,不知道⼤家的经验是什么?然后还有⼀个就是不扯淡,尽快去把东西build 起来,然后让东西去运转起来。我前⼀段时间也写了⼀个段⼦,就是:“你是写 Rust 的,他是写 Java 的,你们这聊了这么久,⼈家 Rust (编译速度慢)
的程序已经编译过了,你 Java 还没开始写。”原版是这样的:“你是砍柴的,他是放⽺的,你们聊了⼀天,他的⽺吃饱了,你的柴呢?”然后最近还有⼀个特别有争议的话题:CTO 应该⼲嘛。就是 CTO 到底该不该写代码,这个也是众说纷纭。因为每⼀个⼈都受到⾃⼰环境的局限,所以每个⼈的看法都是不⼀样的。那我觉得有点像,就是同样是聊天,然后不同⼈有不同的看法。
Test automation
掘金黑客Allow developers to get a unit test results immediately.
Allow developers to run all unit tests in one go.
Allow code coverage calculations.
Show the testing evolution on the dashboards.
Automate everything.
我们现在很有意思的⼀个事情是,迄今为⽌ PingCAP 没有⼀个测试⼈员,这是在所有的公司看来可能都是觉得不可思议的事情,那为什么我们要
指出教室里墙面边线之间的位置关系
这么⼲?因为我们现在的测试已经不可能由⼈去测了。究竟复杂到什么程度呢?我说⼏个基本数字⼤家感受⼀下:我们现在有六百多万个 Test,这是完全⾃动化去跑的。然后我们还有⼤量从社区收集到的各种 ORM Test,⼀会我会提到这⼀点。就是这么多 Test 已经不可能是由⼈写出来的了,以前的概念⾥⾯是 Test 是由⼈写的,但实际上 Test 不⼀定是⼈写的,Test 也是可以由机器⽣成的。举个例⼦,如果给你⼀个合法的语法树,你按照这个语法树去做⼀个输出,⽐如说你可以更换变量名,可以更换它的表达式等等,你可以⽣成很多的这种 SQL 出来。
Google Spanner 就⽤到这个特性,它会有专门的程序⾃动⽣成符合 SQL 语法的语句,然后再交给系统去执⾏。如果执⾏过程中 crash 了,那说明这个系统肯定有 bug。但是这地⽅⼜蹦出另外⼀个问题,就是你⽣成了合法的 SQL 语句,但是你不知道它语句执⾏的结构,那你怎么去判断它是不是对的?当然业界有很聪明的⼈。我把它扔给⼏个数据库同时跑⼀下,然后取⼏个⼤家⼀致的结果,那我就认为这个结果基本上是对的。如果⼀个语句过来,然后在我这边执⾏的结果和另外⼏个都不⼀样,那说明我这边肯定错了。就算你是对的,可能也是错的,因为别⼈执⾏下来都是这个结果,你不⼀样,那⼤家都会认为你是错的。
所以说在测试的时候,怎么去⾃动⽣成测试很重要。去年,在美国那边开始流⾏⼀个新的说法,叫做 “怎么在你睡觉的时候发现 bug”。那么实际上测试⼲的很重要的事情就是这个,就是⾃动化测试是可以在你睡觉的时候发现 bug。好像刚才我们还提到 fault injection ,好像还有 fuzz testing。然后所有
测试的⼈都是⼯程师,因为只有这样你才不会甩锅。
这是我们现在坚信的⼀个事情,就是所有的测试必须要⾼度的⾃动化,完全不由⼈去⼲预。然后很重要的⼀个就是雇最优秀的⼈才,同时给他们挑战,就是如果没有挑战,这些⼈才会很闲,精⼒分散,然后很难合⼒出成绩。因为以现在这个社会⽽⾔,很重要⼀个特性是什么?就是对于复杂性⼯程需要⼤量的优秀⼈才,如果优秀的⼈才⼒不往⼀处使⼒的话,这个复杂性⼯程是做不出来的。我今天看了⼀下龙芯做了⼗年了,差不多是做到英特尔凌动处理器的⽔平。他们肯定是有很优秀的⼈才,但是⽬前还得承认,我们在硬件上⾯和国外的差距还⽐较⼤,其实软件上⾯的差距也⽐较⼤,⽐如说我们和 Spanner 起码差了七年,2012 年 Spanner 就已经⼤规模在 Google 使⽤了,对这些优秀的作品,我们⼀直⼼存敬仰。
我刚才已经反复强调过⾃动化这个事情。不知道⼤家平时写代码 cover 已经到多少了?如果 cover ⼀直低于 50%,那就是说你有⼀半的代码没有被测到,那它在线上什么时候都有可能出现问题。当然我们还需要更好的⽅法去在上线之前能够把线上的 case 回放。理论上你对线上这个回放的越久你就越安全,但是前提是线上代码永远不更新,如果业务⽅更新了,那就⼜相当于埋下了⼀个定时。⽐如说你在上⾯跑两个⽉,然后业务现在有⼀点修改,然⽽那两个⼜没有 cover 住修改,那这时候可能有新的问题。所以要把所有的⼀切都⾃动化,包括刚才的监控。⽐如说你⼀个系统⼀过去,然后⾃动发现有哪些项需要监控,然后⾃动设置报警。⼤家觉得这事是不是很神奇?其实这在 Google ⾥⾯是
司空见惯的事
情,PingCAP 现在也正在做。
湖北医学院
Well… still not enough ?
文学社会学Each layer can be tested independently.
Make sure you are building the right tests.
Don’t bother great people unless the testing fails.
Write unit tests for every bug.
这么多还是不够的,就是对于整个系统测试来讲,你可以分成很多层、分成很多模块,然后⼀个⼀个的去测。还有很重要的⼀点,就是早期的时候我们发现⼀个很有意思的事情。就是我们 build 了⼤量 Test,然后我们的程序都轻松的 pass 了⼤量的 Test,后来发现我们⼀个 Test 是错的,那意味着什么?意味着我们的程序⼀直是错的,因为 Test 会把你这个 cover 住。所以直到后来我们有⼀次觉得⾃⼰写了⼀个正确的代码,但是跑出来的结果不对,我们这时候再去查,发现以前有⼀个 Test 写错了。所以⼀个正确的 Test 是⾮常重要的,否则你永远被埋在错误⾥⾯,然后埋在错误⾥⾯感觉还特别好,因为它告诉你是正确的。
还有,为什么要⾃动化呢?就是你不要去打扰这些聪明⼈。他们本⾝很聪明,你没事别去打扰他们,说“来,你过来给我做个测试”,那这时候不断去打扰他们,是影响他们的发挥,影响他们做⾃⼰的挑战。
这⼀条⾮常重要,所有出现过的 bug,历史上只要出现过⼀次,你⼀定要写⼀个 Test 去 cover 它,那这个法则⼤家应该已经都清楚了。我看今天所在的⼈的年龄,应该《圣⽃⼠星⽮》是看过的,对吧?这个圣⽃⼠是有⼀个特点的,所有对他们有效的招数只能⽤⼀次,那这个也是⼀样的,就保证你不会被再次咬到,就不会再次被坑到。我印象中应该有很多⼈ fix bug 是这样的:有⼀个 bug 我 fix 了,但没有 Test,后来⼜出现了,然后这时候就觉得很奇怪,然后积累的越多,最后就被坑的越惨。
这个是⽬前主流开源社区都在坚持的做法,基本没有例外。就是如果有⼀个开源社区说我发现⼀个 bug,我没有 Test 去 cover 它,这个东西以后别⼈是不敢⽤的。
Code review
At least two LGTMs (Looks good to me) from the maintainers.
Address comments.
Squash commit logs.
Travis CI/Circle CI for PRs.
简单说⼀下 code review 的事情,它和 Test 还是有⼀点关系,为什么?因为在 code review 的时候你会提⼀个新的 pr,然后这个 pr ⼀定要通过这个 Test。⽐如说典型的 Travis CI,或者 CircleCI 的这种 Test。为什么要这样做呢?因为要保证它被 merge 到 master 之前你⼀定要发现这个问题,如果已经 merge 到 master 了,⾸先这不好看,因为你要 revert 掉,这个在 commit 记录上是特别不好看的⼀个事情。另外⼀个就是它出现问题之前,你就先把它发现其实是最好的,因为有很多⼯具会根据 master ⾃动去 build。⽐如说我们会根据 master 去⾃动 build docker 镜像,⼀旦你代码被 commit 到 master,然后 docker 镜像就出来了。那你的⽤户就发现,你有新的更新,我要马上使⽤新的,但是如果你之前的 CI 没有过,这时候就⿇烦了,所以 CI 没过,⼀定不能进⼊到 CD 阶段。
Who to blame in case of bugs?
The entire team.
另外⼀个观念纠正⼀下,就是出现 bug 的时候,责任是谁的?通常我见过的很多⼈都是这样,就说“这个 bug 跟我没关系,他的模块的 bug”。那PingCAP 这边的看法不⼀样,就是⼀旦出现 bug,这应该是整个 team 的责任,因为你有⾃⼰的 code review 机制,⾄少有两个以上的⼈会去看它这个代码,然后如果这个还出现问题,那⼀定不是⼀个⼈的问题。
除了刚才说的发现⼀些 bug,还有⼀些你很难定义,说这是不是 bug,怎么系统跑的慢,这算不算 bug,怎么对 bug 做界定呢?我们现在的界定⽅式是⽤户说了算。虽然我们觉得这不是 bug,这不就慢⼀点吗,但是⽤户说了这个东西太慢了,我们不能忍,这就是 bug,你就是该优化的就优化。然后我们团队⾥⾯出现过这样的事情,说“我们这个已经跑的很快了,已经够快了”,对不起,⽤户说慢,⽤户说慢就得改,你就得去提升。总⽽⾔之,标准不能⾃⼰定,当然如果你⾃⼰去定这个标准,那这个事就变成“我这个很 OK 了,我不需要改了,可以了。”这样是不⾏的。
Profiling
Profile everything, even on production
once-in-a-lifetime chance
奥林匹克大逆转Bench testing
另外,在 Profile 这个事情上⾯,我们强调⼀个,即使是在线上,也需要能做 Profile,其实 Profile 的开销是很⼩的。然后很有可能是这样的,有⼀次线上系统特别卡,如果你把那个重启了,你可能再也没有机会复现它了,那么对于这些情况它很可能是⼀辈⼦发⽣⼀次的,那⼀次你没有抓住它,你可能再也没有机会抓住它了。当然我们后⾯会介绍⼀些⽅法,可以让这个能复现,但是有⼀些确实是和业
务相关性极强的,那么可能刚好⼜碰到⼀个特别的环境才能让它出现,那真的可能是⼀辈⼦就那么⼀次的,你⼀定要这次抓住它,这次抓不住,你可能永远就抓不住了。因为有些犯罪它⼀辈⼦只犯⼀次,它犯完之后你再也没有机会抓住它了。
Embed testing to your design
Design for testing or Die without good tests
Tests may make your code less beautiful
再说测试和设计的关系。测试是⼀定要融⼊到你的设计⾥⾯,就是在你设计的时候就⼀定要想这个东西到底应该怎么去测。如果在设计的时候想不到这个东西应该怎么测,那这个东西就是正确性实际上是没法验证的,这是⾮常恐怖的⼀件事情。我们把测试的重要程度看成这样的:你要么就设计好的测试,要么就挂了,就没什么其它的容你选择。就是说在这⼀块我们把它的重要性放到⼀个最⾼的程度。
>>>>>>>>>>##
本话题系列⽂章整理⾃ PingCAP Infra Meetup 第 26 期刘奇分享的《深度探索分布式系统测试》议题现场实录。⽂章较长,为⽅便⼤家阅读,会分为上中下三篇,本⽂为中篇。
-接上篇-
当然测试可能会让你代码变得没有那么漂亮,举个例⼦:
这是知名的 Kubernetes 的代码,就是说它有⼀个 DaemonSetsController,这 controller ⾥⾯注⼊了三个测试点,⽐如这个地⽅注⼊了⼀个handler ,你可以认为所有的注⼊都是 interface。⽐如说你写
⼀个简单的 1+1=2 的程序,假设我们写⼀个计算器,这个计算器的功能就是求和,那这就很难注⼊错误。所以你必须要在你正确的代码⾥⾯去注⼊测试逻辑。再⽐如别⼈ call 你的这个 add 的 function,然后你是不是有⼀个 error?这个 error 的问题是它可能永远不会返回⼀个 error,所以你必须要⼈⾁的注进去,然后看应⽤程序是不是正确的⾏为。说完了加法,再说我们做⼀个除法。除法⼤家知道可能有处理异常,那上⾯是不是能正常处理呢?上⾯没有,上⾯写着⼀个⽐如说 6 ÷ 3,然后写了⼀个 test,coverage 100%,但是⼀个除零异常,系统就崩掉了,所以这时候就需要去注⼊错误。⼤名⿍⿍的 Kubernetes 为了测试各种异常逻辑也采⽤类似的⽅式,这个结构体不算长,⼤概是⼗⼏个成员,然后⾥⾯就注⼊了三个点,可以在⾥⾯注⼊错误。
那么在设计 TiDB 的时候,我们当时是怎么考虑 test 这个事情的?⾸先⼀个百万级的 test 不可能由⼈⾁来写,也就是说你如果重新定义⼀个⾃⼰的所谓的 SQL 语法,或者⼀个 query language,那这个时候你需要构建百万级的 test,即使全公司去写,写个两年都不够,所以这个事情显然是不靠谱的。但是除⾮说我的 query language 特别简单,⽐如像 MongoDB 早期的那种,那我⼀个“⼤于多少”的这种,或者 equal 这种条件查询特别简单的,那你确实是不需要构建这种百万级的 test。但是如果做⼀个 SQL 的 database 的话,那是需要构建这种⾮常⾮常复杂的 test 的。这时候这个 test ⼜不能全公司的⼈写个两年,对吧?所以有什么好办法呢?MySQL 兼容的各种系统都是可以⽤来 test 的,所以我们当时兼容 MySQL 协议,那意味着我们能够取得⼤量的 MySQL test。不知道有没有⼈统计过 MySQ
L 有多少个 test,产品级的 test 很吓⼈的,千万级。然后还有很多ORM,⽀持 MySQL 的各种应⽤都有⾃⼰的测试。⼤家知道,每个语⾔都会 build ⾃⼰的 ORM,然后甚⾄是⼀个语⾔的 ORM 都有好⼏个。⽐如说对于 MySQL 可能有排第⼀的、排第⼆的,那我们可以把这些全拿过来⽤来测试我们的系统。
但对于有些应⽤程序⽽⾔,这时候就⽐较坑了。就是⼀个应⽤程序你得把它 setup 起来,然后操作这个应⽤程序,⽐如 WordPress,⽽后再看那个结果。所以这时候我们为了避免刚才⼈⾁去测试,我们做了⼀个程序来⾃动化的 Record—Replay。就是你在⾸次运⾏的时候,我们会记录它所有执⾏的 SQL 语句,那下⼀次我再需要重新运⾏这个程序的时候怎么办?我不需要运⾏这个程序了,我不需要起来了,我只需要把它前⾯记录的SQL record 重新回放⼀遍,就相当于是我模拟了程序的整个⾏为。所以我们在这部分是这样做的⾃动化。
那么刚刚说了那么多,实际上做的是什么?实际上做的都是正确路径的测试,那⼏百万个 test 也都是做的正确的路径测试,但是错误的路径怎么办?很典型的⼀个例⼦就是怎么做 Fault injection。硬件⽐较简单粗暴的模拟⽹络故障可以拔⽹线,⽐如说测⽹络的时候可以把这个⽹线拔掉,但是这个做法是极其低效的,⽽且它是没法 scale 的,因为这个需要⼈的参与。
然后还有⽐如说 CPU,这个 CPU 的损坏概率其实也挺⾼的,特别是对于过保了的机器。然后还有磁
盘,磁盘⼤概是三年百分之⼋点⼏的损坏率,这是⼀篇论⽂⾥⾯给出的数据。我记得 Google 好像之前给过⼀个数据,就是 CPU、⽹卡还有磁盘在多少年之内的损坏率⼤概是什么样的。
还有⼀个⼤家不太关注的就是时钟。先前,我们发现系统时钟是有回跳的,然后我们果断在程序⾥⾯加个监测模块,⼀旦系统时钟回跳,我们马上把这个检测出来。当然我们最初监测出这个东西的时候,⽤户是觉得不可能吧,时钟还会有回跳?我说没关系,先把我们程序开了监测⼀下,然后过段时间就检测到,系统时钟最近回跳了。所以怎么配 NTP 很重要。然后还有更多的,⽐如说⽂件系统,⼤家有没有考虑过你写磁盘的时候,磁盘出错会怎么办?好,写磁盘的时候没有出错,成功了,然后磁盘⼀个扇区坏了,读出来的数据是损坏的,怎么办?⼤家有没有 checksum ?没有checksum 然后我们直接⽤了这个数据,然后直接给⽤户返回了,这个时候可能是很要命的。如果这个数据刚好存的是个元数据,⽽元数据⼜指向别的数据,然后你⼜根据元数据的信息去写⼊另外⼀份数据,那就更要命了,可能数据被进⼀步破坏了。
所以⽐较好的做法是什么?
Fault injection
Hardware
disk error
network card
cpu
clock
Software
file system
network & protocol
Simulate everything
模拟⼀切东西。就是磁盘是模拟的,⽹络是模拟的,那我们可以监控它,你可以在任何时间、任何的场景下去注⼊各种错误,你可以注⼊任何你想要的错误。⽐如说你写⼀个磁盘,我就告诉你磁盘满了,我告诉你磁盘坏了,然后我可以让你 hang 住,⽐如 sleep 五⼗⼏秒。我们确实在云上⾯出现过这种情况,就是我们⼀次写⼊,然后被 hang 了为 53 秒,最后才写进去,那肯定是⽹络磁盘,对吧?这种事情其实是很吓⼈的,但是肯定没有⼈会想说我⼀次磁盘写⼊然后要耗掉 53 秒,但是当 53 秒出现的时候,整个程序的⾏为是什么?TiDB ⾥⾯⽤了⼤量的 Raft,所以当时出现⼀个情况就是 53 秒,
然后所有的机器就开始选举了,说这肯定是哪⼉不对,重新把 leader 都选出来了,这时候卡 53 秒的哥们说“我写完了”,然后整个系统状态就做了⼀次全新的迁移。这种错误注⼊的好处是什么?就是知道当出错的时候,你的错误能严重到什么程度,这个事情很重要,就是predictable,整个系统要可预测的。如果没有做错误路径的测试,那很简单的⼀个问题,现在假设⾛到其中⼀条错误路径了,整个系统⾏为是什么?这⼀点不知道是很吓⼈的。你不知道是否可能破坏数据;还是业务那边会 block 住;还是业务那边会 retry?
以前我遇到⼀个问题很有意思,当时我们在做⼀个消息系统,有⼤量连接会连这个,⼀个单机⼤概是连⼋⼗万左右的连接,就是做消息推送。然后我记得,当时的 swap 分区开了,开了是什么概念?当你有更多连接打进来的时候,然后你内存要爆了对吧?内存爆的话会⾃动启⽤ swap 分区,但⼀旦你启⽤ swap 分区,那你系统就卡成狗了,外⾯⽤户断连之后他就失败了,他得重连,但是重连到你正常程序能响应,可能⼜需要三⼗秒,然后那个⽤户肯定觉得超时了,⼜切断连接⼜重连,就造成⼀个什么状态呢?就是系统永远在重试,永远没有⼀次成功。那这个⾏为是不是可以预测?这种错误当时有没有做很好的测试?这都是⾮常重要的⼀些教训。
硬件测试以前的办法是这样的(Joke):

本文发布于:2024-09-21 18:38:02,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/388264.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:测试   系统   可能   时候   没有   程序   分布式   模块
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议