@Lenciel

关于微服务实践的一次分享

最近在 EGO 里做了一次关于微服务架构的分享,keynote写好了,思路在这里梳理一下。

不讲干货

码农的四大错觉里面,排第一的就是干货有用。

我见过很多人在技术帖子或者技术大会里给其他人打「没有干货」的标签。

太「水」的东西大家是应该抵制。

但干货也是思维的结果,而非过程,对本座来说,就好像风干了之后的罐装海味,不好吃。

腾讯,阿里是怎么实施微服务架构的,介绍得再「干」,你听了再兴奋,拿回去多半也没法落地。

构建软件系统,看起来是一群理性的人,进行的一件理性的工作。

但人类的理性在于接受自身的不理性和事物的无规律:而不是照本宣科。

因为根本没有可以长期驻留的「本」,万事万物都在高速运动和变化着,没有什么方法论可以一直有效。

所以 Martin Fowler 说,「唯一成立的业务逻辑就是业务没有逻辑」。

好的系统,都不是被设计出来,而是被恰到好处的人在恰到好处的流程里慢慢抚育出来的。

所以主要是分享一些我们实施过程中的故事,遇到的问题和后续的一些想法。

MSA是什么?

微服务架构(Microservice Architecture,简称 MSA)的思想其实不是 Martin Fowler 提出的,而是存在已久。只不过 Martin Fowler 和 James Lewis 在那篇广为传颂的文章里给它下了个简明扼要的的定义:

In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.

我经常跟同学们开玩笑说,随便在软件园拦住一个人,都可以跟你聊半个小时「微服务」,因为这里提到的一些说法看起来非常具体:

  • 细粒度的服务
  • 独立进程
  • 围绕业务建模
  • 轻量级通信
  • 去中心化管理

但一旦进入了实际操作,这些微言大义的概念,怎么落地到系统开发里面,就不是每个人都明白的了。其实回过头来看看,Martin Fowler 为什么说「microservice architectural style(微服务架构风格)」而不像很多人只是说「microservice(微服务)」,是很值得玩味的。

是风格不是教条

每个公司进行架构改造,都是业务特点团队情况和技术栈等多方面因素综合决定的系统工程。微服务架构是一种「风格」,而不是可以按图索骥的「教条」。

不是什么新科技

MSA 里面提到的很多想法,追究起来都源远流长。「micro-web-service」的提法 2005 年就有,「microservice」这个叫法,也早在 2011 年威尼斯的软件会议里面就开始被使用了。

整个思路的形成和演进里面,「[Unix-like](https://en.wikipedia.org/wiki/Pipeline_(Unix)」或者是「Unix Way」被反复提及。James Lewis 第一个比较正式的关于微服务架构的演讲就叫「Microservices - Java, the Unix Way」。

这是因为 Unix 的很多设计哲学,比如「small is beautiful」或者「make each program do one thing well」,都深深的影响着微服务架构的设计思想。

甚至我觉得 Unix 的「everything is a file」,对应过来正好是「everything is a service」。

不是SOA换了个壳

很多像我一样从业已久的人,听到新名词的时候总是持抵抗和怀疑态度的。MSA 不过是 Thoughtworks 把 SOA 换了个名字出来坑钱而已,,是一个很容易入的坑。

实际上,它们确实有关系,但并不是 SOA 换了个壳。你可以把 MSA 想成是 SOA 的一种实践方式,正如 Scrum 是 Agile 的实践方式一样。只不过这种实践方式,和以前的各种实践方式相比,最大的不同就是去掉了 ESB 这样的总线,让系统的各个部分可以相对独立的被设计、开发、部署和运维。

MSA究竟是什么?

Technology is the answer, but what was the question?

  • Cedric Price (1965)

前面说了很多 MSA 不是什么,那么它究竟是什么?不妨从我们究竟为什么需要它这个维度来思考。

为什么单体的设计不能满足需要了?我们可以从率先进行 MSA 实施的公司来看。

Nike,Twitter,Netflix,以及从来不说自己在做微服务架构,但其实是做得最好的 Google,它们的业务差别其实很大。比如 Nike 和 Twitter,一个业务很复杂,一个业务很简单。

真正的共同之处在于它们构建的都是大型分布式系统。

什么是分布式系统

本座在面试的时候常常问,「请问,什么样的系统,是分布式系统?」

毕竟,我们不是所有人都在构建 Twitter 那样复杂的系统。

Don't touch me

fig1.1 Twitter的服务间依赖图

有很多有趣的答案,但我们不妨看看图灵奖得主,分布式系统的先驱Leslie Lamport是怎么说的:

A distributed system is one in which the failure of a computer you didn’t know existed can render your own computer unusable.

这句话是几十年前说的,那时候有计算能力的设备还只有computer而已,现在把手机等各种设备都算上的话,如果一台你不知道的计算设备挂了,会影响你的设备的使用,就叫分布式系统的话,我们可以想想,有多少系统应该算是分布式系统呢?

淘宝,京东,腾讯,百度?

滴滴,知乎,摩拜,微信?

可以说,移动互联网发展到今天,几乎所有的系统,都是分布式系统。

分布式系统的难点

为什么系统都变成了分布式系统?

因为在处理和分析数据时,最理想的计算环境是这样的:一台有无限存储和计算能力的「超级计算机」,可以提供无穷大的存储容量,并且可以将计算时间降低至无穷小。

可是像《银河系漫游指南》里全宇宙全时空第二强的「深思」这样的计算机并不存在,因此现实里面我们需要进行两个维度的扩展:

  • 纵向的:提升单个设备的软硬件处理能力,比如我们的各种超级计算机的研究
  • 横向的:构建分布式系统,通过多台计算机来提供一个处理能力更强的系统

对于大多数的场景,横向的扩展显然是更加现实的。但这样的系统在构建过程中,有一些固有的难点,比如物理上的,比如理论上的。因为这些难点,分布式系统有两个很难解决的问题:异步性(Asynchrony)和局部失效(Partial Failure)。

异步性

异步性表征的物理上的难点,也就是物理节点的增加带来的难点:

  • 系统整体出错概率增大
  • 节点间通信量增加
  • 节点间距离增加

异步性会导致不确定性(Nondeterminism),包括 timing 上的,也包括 sorting 上的。

Grace Hopper(第一个编译器的作者),喜欢给自己每个学生发一条 11.8 英寸的电线:这是电一纳秒能够跑的距离,他想提醒学生脑子里面要有时延。

Jeff Dean 在斯坦福的那个著名的讲座里面提出了每个程序员都应该知道的一些时延数据

There is no Now

局部失效

局部失效表征的是理论上的难点:

  • 如何处理局部失效:节点失效、网络分区失效、拜占庭失效等情况下,系统的执行和操作不应受到失效的影响;
  • 失效了如何保持一致:系统中相关数据间的逻辑关系应当是正确和完整的;

在 1985 年的时候,Fischer, Lynch 和 Patterson 就提出了分布式系统最重要的理论之一:FLP不可能性,毁灭了构建分布式超级计算机系统的幻想:

在假设网络可靠、计算节点只会因崩溃而失效的最小化异步模型系统中,仍然不存在一个可以解决一致性问题的确定性算法。

但随后,Eric Brewer 等人提出了 CAP 定理(如今它比 FLP 更加深入人心):

分布式计算系统不可能同时确保一致性(Consistency)、可用性(Availability)和分区容忍性(Partition tolerance)。

在这种三者只能取其二的思想指导下,大量的系统和协议被构建出来。

根本问题

无论是异步性还是局部失效,背后的根本问题是,任何软硬件系统都会崩坏。这两年 Google 的 Spanner 被很多人错误地称为「解决了分布式系统的一致性问题」。

实际上,Spanner 的 TrueTime 组件并不提供一个全局唯一的时钟:它给你一个当前值可能的偏差,并且在设计上它就明确,这个偏差可能在 1-7 毫秒。

换句话说,虽然是 Google,虽然它用上了 GPS+原子钟的方案,时延仍然是这样的大。如果再考虑 x86 的时钟会被负载、热量、功率等各种因素影响,可以认为,Google 的 Spanner 并不是尝试去解决分布式系统的问题:正好相反,它是承认,然后面对里面的问题。

所有的靠谱的系统设计者都知道面对这些问题才是正确的做法:以 Spanner 依赖的 GPS 时钟为例,它靠着 30 颗卫星保持运行,只需要其中的 4 颗就可以工作,并且每颗卫星的关键部件,比如原子钟,都是有冗余的。

微服务架构

可以认为,MSA 是在下面三个方面不断发展的基础上,渐渐坐实成为一种架构的:

  • 必要的组件:服务注册与发现、熔断等等
  • 有用的方法:DDD,SAGA,CQRS 等等
  • 日益成熟的新技术:容器,No-SQL 等等

所以,MSA 的定义可以是:

在分布式系统建设中,为了应对需求的快速变更,在高增速的大公司内部构建高效、自治、响应迅速的「创业风格」团队的一些尝试。

换句话说,它是涵盖研发、测试、部署、监控、运维各方面的整个生命周期的生态,隐含着一些工具、方法和构建系统的思路。不仅意味着业务的切割,也要求技术栈的解构和重建。

如何实施

人永远是最重要的环节,因此在进行 MSA 的落地之前,不妨问问自己:

  • 有多少研发?里面有多少懂运维?
  • 有多少团队?他们互相信任吗?
  • 有多少产品?多少业务系统?
  • 哪些需要纳入微服务化的计划?
  • 如何去在内部销售基础设施?

康威定律的内容,值得每个架构师和技术管理者学习

流程

CD/CI

MSA 技术层面上每一目的的达成,几乎都是多组件、多层级、多技术的共同作用。因此,要积极地、容易地接纳新业务,新平台,新技术栈,就必须有比较顺畅的流程,特别是 CD、CI 流程。

测试

The Hard Thing About Hard Things大家都可以去看看,非常有意思。

分布式系统的两大难题带来的是复杂度和状态空间的指数级增加。

比如一个单体的应用,了不起就是 5 个实例跑在一个 HA Proxy 背后,测试的时候你可以测 5 个实例。

如果它被拆成了 10 个服务,每个服务跑了 12 个 docker 实例起来呢?

传统的测试方法肯定是不行的。

因此,如何进行「契约测试」是测试技术发展的一个新动向,Lamport 本人就贡献了TLA+来进行分布式系统的形式化的验证

如何这样的投入对于团队太奢侈了怎么办?感觉可以看看这篇论文,里面有很多数据非常有趣,看完之后对测试设计帮助很大:

Three nodes or less can reproduce 98% of failures. 35% of catastrophic failures are caused by very basic things. Testing error handling code could have prevented 58% of catastrophic failures.

另外,除开常规的测试手段,分布式系统大量的使用 Fault Injection 或者 Game Days 这种方式来进行测试。比如 Netflix 就公布了自己的Simian Army工具,Riemann的作者 Aphyr 还用 Clojure 写了个工具叫Jepsen

Game Days 主要指通过演习来让 Partial Failure 确实发生。比较著名的有 Google 的 Wheel of Misfortune,再比如 Stripe 在自己的 Redis 主节点上来了一发kill -9之后发现了 Redis 的一个 bug,于是把这样的乱来变成了内部传统。

技术

解决了前面这两个方面的问题,要落地微服务其实已经成功了大半。

关于技术方面,详见 keynote 吧。

苏大爷

1

苏大爷是广汉市自来水公司的门卫。

父亲调到这单位,举家搬进跟办公楼一院儿的宿舍,从拉家具的卡车上下来时,他给我搭了把手。

所以苏大爷是我在广汉认识的第一个人。

那会儿他应该就已过了退休年龄,两鬓斑白,满脸皱纹,行事缓慢,声音温和。

可能怕自己动作一大,那刀刻般的皱纹里嵌的愁苦,就要抖落一地,叮当作响,为人所知。

所以他总是静静地看着书。

门卫室的桌上,床上,地上,到处是书,我常去蹭。

一来二去,熟了,他就爱推荐些书给我看。

「这本《多余的话》不错」,递给我的时候,他指着墙上那排副总以上职位的领导才有,刻着名字金光闪闪的不锈钢信件盒子说,「男人个个都想把自己的名字刻在什么东西上。肯把一辈子唱成破败挽歌的瞿秋白,真正是老实敞亮,不卑不亢。」

我拿去读了,不喜欢,但记着了这革命先烈和课本上说的不一样:被枪毙时,不光惦记着马列主义,还惦记着中国的豆腐乃是世界第一,了不起。

更喜欢听他讲自己的故事。

2

苏大爷老家在三水镇石关村,排行老二,大家叫他苏老二。

因为听起来像梭老二,苏老二不满意,让大家叫他苏二。

家里穷,苏二很小就成了地里的主力。

农村人,结婚早,没多久就要了娃。

但他去交粮,地主当家的看他眉清目秀,手脚麻利,人也机灵,就收了他去账房,从更夫杂役,做到候补学徒,也就是「耽搁」。

庄稼地里的顶梁柱,到账房却是童工,没有工资,只领「花红」,每月两个银元。一年下来,能买 100 斤大米,或者 14 斤猪肉。

人从此住在镇上,寄回去的钱,母子俩勉强能过,家却慢慢破旧了。

草顶的泥房,墙歪得只能用木杠支起来。偌大的院子,几只瘦得被风推来推去的下蛋鸡,跑在枝条漫不经心叠着的柚子树下。树上挂的果,又小又酸,像是在闹脾气。

但苏二不想回家种地。他在账房里认了字,读了书,开了眼。转成一级学徒后,靠着为人厚道,做事尽心,一手算盘打得风快,他慢慢升级,到 22 岁的时候,已经是「五级学徒」,下一步就能做个「内账」,是正经的账房先生了。

他定时寄钱,很少回家,孩子不怎么认识他。

他倒是认识了玉清。

3

那时候,春天还很分明,总织着雨幕。

玉清的母亲来找当家的姨太太拉家常,玉清就在院里闲逛。

四目相对时,他心里一惊,就不敢再看。

晚上睡觉,心还惊着。想了一宿,太过用力,天边白起来,脑子里影影绰绰,已经想不起姑娘的长相。

几天后,账房里,他正在忙着,一抬头,面前却是玉清。

这次看清楚了,眉黛春山,眼含秋水,款款地走过来,光芒却映得账房里像生了火。

她说,「杀牛场边上有片夹竹桃,晚饭后去那儿等着,陪我聊会儿天。」

声音软软悠悠,没等他回话,人已经不见了。

他抬头往窗外望,雨还下着,心里面却也像生了火,爽利起来。

两人第一次约会,第一次牵手,第一次拥抱,都在杀牛场那片夹竹桃里。

不是很好的环境,光是空气里,就有夹竹桃汁液的腐臭味,牲口鲜血的腥味,挺难闻。

但学徒和小姐的私会,也没有太多选择。

每次最难的是分开。

玉清家里有父母候着,不能待太久。

每次她不停摆弄着自己的手,就是要走了。

苏二也就摆弄起对方的手来。

等到玉清心一硬,站起来要走,苏二就顺手把她拉回来坐下。

终于有次,拉扯中没忍住,他说:「玉清,我攒些钱,你给家里说说,我们在一起吧。」

玉清听了,心里烫着,却不接话。

「你怕的是我有老婆孩子?他们跟了我,是苦命人。每个月寄回家的钱,还得寄。但我还有结余,还会更卖力气,这么久了,你看得出来,我心里只有你,我们会过上好日子。」

玉清听了,心里更烫,但想到精明严厉的父亲,一腔喜悦都变了泪,呜呜地跑开了。

那晚的风,也呜呜地,吹了一宿。

4

不久就解放了,当家的挨了枪子儿,已经离婚的苏二,作为地主的狗腿子,被关进了青城山下的监狱。

玉清的父亲上交了宅院、田地和财产,当了干部,住进县城。

苏二放出来这天,也是春天,也下雨。

他打听着到了玉清家,在门口踌躇了半响,终于敲了门。

门缝里,玉清呆了片刻,就把他迎进了客厅坐下。两个人你看我,我看你,不说话。

她长开了,像饮饱了的水仙,比以前更舒展。只是衣裳换了统一的革命式样,妩媚都藏着。

而他进了一趟班房,蓬头垢面之外,身子也消瘦了不少。

但眉宇间那英气都还在,她心里觉着些安慰。

「他们要下班了,我给你做点儿吃的吧。」

找来找去,家里只有几个鸡蛋,一点儿牛奶。

「你还是像以前一样喝不惯牛奶吧,我给你煮两个蛋。」

很快,盘子就端上茶几,玉清吐了吐舌头:「有一个扔下去的时候,破了。」

苏大爷看着盘里的鸡蛋,一颗完整,一颗爆开,拥在一起。想着自己终于有了人照顾,就湿了眼眶,要拉玉清的手。

玉清躲了,却没怪他,定定地看着他的手,问:「你看你这手,是干了啥?」

「来的时候,想给你带点儿东西,又没钱。山脚下看到些毛栗子,就摘了。」

苏二说着,就把栗子从裤兜里面掏出来。一个一个,小小的,刺却硬挺,扎进了兜里,掏得很费劲。好不容易掏完,挑了个最大的刨开,小得像一粒青杠尖儿,苏二的眉头就皱起来。

玉清终于忍不住,握起他的手,轻轻摩挲起来。

窗外的雨下得疾,紧一阵,再紧一阵,压得屋里也没了声响。

分开的时候,玉清拿了把伞,把他送到楼下,又从兜里掏出些钱,都递给了他,说:

「开春了,你回家看看孩子吧。」

苏二接了伞,推开钱,说:

「我还来……」

玉清没说话,冲他摆了摆手,看他转过身走进雨里不见了,才柔柔地落下泪来。

5

苏二会算账,找活干不难,很快就进了个公私合营的公司,叫鸡鸭鹅蛋厂,做会计。

那时候什么都是公家的,没有品牌,名字就坦白,比如鸡鸭鹅蛋厂,做的就是禽类的肉和蛋的加工。

厂不大,帐也简单。好处是,离玉清上班的国营食品公司近,常能看见她。

坏处是,离这么近,从没看到过她,没过两天,倒是听说了她的婚讯。

苏二明白了为什么见不着她,喝了酒,倒在租来的房里,死死地睡。

厂长少了骨干,差了知情人来劝。

「刘玉清,富农子弟,一家人都前途不明。你呢,离过婚,又有前科,两个人凑一起是要干嘛?别人给她介绍的这个,军队的,待遇好,有前途,三代贫农,根正苗红。」

苏二挣扎着起来,脸上和心上都被撕开了口子,像受伤后吃不住疼的小兽,满城乱走。

一直走到城边鸭子河的坝上,再无路可去,才伤伤心心哭了出来。

鸭子河滚滚东去,稳稳地,没有回应。

两天后,苏二回到了厂里上班。人还是那个人,业务能力强,为人也公道,不久就做了财务室主任。

但人又不是那个人了,攒下些钱,就跑到鸭子河边买了个窄小破败的院坝,自己砌了个砖房,离群索居。

原本清爽干净的人,也变得嗜烟好酒,常把自己锁在屋里熏着看书,终日无语。

唯一比较有生气的是院门口栽的那两株夹竹桃,一白一红,开花时,常有小孩儿来折。苏二也不恼,眯了眼笑着说:「特别好看吧?特别好看。但小朋友要小心,别玩到嘴巴里面了。」

就这样春去秋来,不知道第几个冬天,城里突然乱起来,有很多传闻,随风乱窜。那时候的冬天,还很冷,总下雪。皑皑的白雪,映着鲜红的标语,苏二总觉得触目惊心。

有一天,下班回家,苏二见玉清孤零零守在自己门口。

「找我?」

玉清低了头,沉默不语。

又问:

「出什么事儿了,你说……」

玉清见他一脸的关切,知道他还记挂着自己,这才开口:

「我男人带人,把我爸妈拉出去游街,还动了手。老年人想不通,吞了火柴头。」

苏二红了脖子,要去找人算账。

「苏二」,这次是玉清拉了他的手,「我男人也是被逼的,他和我一样害怕。」

「不怪他?」

「都是命嘛。」

「还怕吗?」,苏二的声音温柔下来。

玉清的眼泪忍不住,挂了一脸。

两人拉了手,坐在一起,窗外的雪下得很大,天黑后就有些冷。苏二把玉清抱到被窝里捂着,像抱着襁褓里的婴儿。

早上起来,天晴了。推开窗,云山满目,烟树模糊,喧闹的世界变得遥远起来。苏二给玉清买了全蛋金丝面,自己弄的佐料,过一下油的香菇、肉末,配院角里刚扯的绿叶红根的菠菜,灌了一大碗鸡汤,香到院门都关不住了,再撒上切碎的新葱。玉清吃完,热气蒸得眉头舒展开来,整个人闪闪发亮。

「我要回去了。」

「能不走了吗?」

「我身份不好,会害着你。这个时候,做这样的事情,也会害着他。」

「你再走,才会害着我。」

玉清不说话,在心里面叹了口气,苏二听得真切,只好说:

「那我送你吧。」

雪已经开始化了,踩起来,湿湿嗒嗒,叽叽嘎嘎,让人从牙一直酸到心里。走到箭道子,前面就进城了,不好再送。苏二看到张麻子的照相馆开着,就要玉清一起合个影。

「给我留张照片吧,偶尔能翻出来看看。」

最后还是没照。

玉清这次走后的事,苏大爷没讲过。

只说自己的院子,到文革时,政府想用,就交了出来,只留着砖房自住。后来整个东南角这一片,都变成了自来水公司,他的鸡鸭鹅蛋厂,被两个老板承包,变成了二元食品厂。

私营企业和之前厂里,做起帐来,不太一样,他也就不再去上班,到自来水公司做了门卫。

我望着门外那两棵三四米高的夹竹桃,问:「杀牛场移过来的?」

「好看吧?不仅好看,还吸尘土。」

「不怪她?」

「都是命嘛。」

高中的时候,我家搬到顺德路人民医院的宿舍,就和苏大爷断了联系。去年清明回家,入了龙泉山公墓大门,拾阶而上,走到某一排的时候,父亲停下一指,说,以前你常去借书的那门卫,苏大爷,也埋这儿。

「哦?」,我顺着他的手看过去,坟头延绵不绝,并不知道究竟是埋在哪儿。

「好些年前的事儿了。这老头一生未娶,也没有子女,在公司干了一辈子,大家都觉得他人好。所以公家出了些,大家捐了些,给他葬了。你多来看看你爷爷和外公,早就知道了。」

烧完香,没忍住,我折回那一排,寻了一通,找到了他的墓碑。

欣赏瞿秋白的苏大爷,和瞿秋白一样,名字还是被刻在了碑上,未能免俗。

我山上山下找了一遍,没找到夹竹桃,只好折了一枝将败未败的桃花,插在碑前。

天色渐晚,碑影变得厚重,罩了桃花。

我呆呆看着,感觉是苏大爷抱了爱人入怀,紧紧地捂。