阿里妹导读:毕玄是阿里巴巴资深技术专家,07年加入阿里,一手打造了HSF,十多年来更见证参与了阿里在基础技术上的演进与发展。他觉得系统设计是远比 Java 编程技能更难的培训,很容易变成务虚课。为了挑战难题,毕玄决定大胆尝试在内部搞了个民间培训。于是就有了今天的文章,希望这些深入浅出的解读能给架构师们带来一些收获。
阿里巴巴资深技术专家 毕玄
本文转自毕玄老师个人公众号:hellojavacases(不常更新,一更就是经典)
系统设计我一直觉得是远比Java编程技能更难多了的培训,很容易变成务虚课,全是一堆理论,所以,以前从来不敢尝试做这方面的培训,今年由于一些情况,决定大胆尝试下,就在内部搞了个民间的培训,还真有不少同学捧场。在开始上这门课后,我觉得收获最大的搞不好是我自己,整理思路,从和学员的互动中学习到了很多,能更好地对系统设计的一些方法论进行抽象总结,所以我对这些捧场的学员们说:其实是我们一起在共创系统设计的课程 。
我给系统设计培训定的目标是:
而要达到这样的效果,怎么去上这堂课挑战其实是不小的:想要传达的思考框架到底是什么?怎么样变成不是纯粹的理论,务虚的传达,从而让大家能更好的掌握并真正的去运用这个思考框架?以前真的没仔细的思考过系统设计的思考框架的问题,其实吧很多的系统设计的模板就是一个思考框架,但在不理解的情况下,是很难应用好的。
回顾了下自己做过的几个系统的设计,发现现在自己在做系统设计的时候确实是会按照一个套路去做,这个套路就是:
系统设计的目的->系统设计的目标->围绕目标的核心设计->围绕核心设计形成的设计原则->各子系统,模块的详细设计。
1) 系统设计的目的
是指做这个系统设计的目的到底是什么,很多人在做系统设计时,是搞不清为什么要做一个新系统的设计,或者为什么要做一个系统的重构/演进的设计,如果搞不清楚这个目的,后面的系统设计上是很容易形成偏差的,导致本来是为了解决一个问题,要去做新的系统或重构/升级旧的系统,但最后完全脱离了初心。
另外,还有很重要的一点是,一个大架构师是需要给很多人讲解系统设计的,只有理解并讲清了系统设计的目的,团队才能更好的去实现。
2) 系统设计的目标
围绕上面的目的,能不能形成一些可衡量的目标,从而确保最终系统实现和最初的目的不要出现太大的偏差,相信很多人都经历过最终的系统实现和系统设计偏差极大的现象,主要的原因基本都是没有制定衡量系统设计的目标,并在系统设计上让系统能透出这些目标的情况。
3) 围绕目标的核心设计
这步最重要的就是通过设计如何去实现上面的目标,这个环节中技术的专业、视野、全面的考虑、权衡取舍的主观原则、解题的思路,这是形成最后的核心设计的关键。
在核心设计的这个阶段中,会产生一些新的衡量设计最后实现情况的目标,这些也都要增加到系统设计中,确保最后的实现和设计的偏差度是可视的。
4) 围绕核心设计形成的设计原则
有了上面的核心设计后,可以真正的形成一些设计原则,确保后面的子系统/模块的详细设计中能够遵循,并在详细设计中体现出来,这样才能让整个大的系统设计的一致性。
5) 各子系统/模块的详细设计
这个部分我倒觉得难度不会太大,毕竟有了前面的铺垫,就是解好一个更小范围的题,程序员群体在解题能力上通常是不错的,所以我一直觉得数学功底好是程序员的基本,数学就是典型的解题的学科。
ps: 这就是做分享/培训的好处,借机仔细整理自己的碎片,从而形成体系。
关于怎么更好的让大家能掌握并运用这个思考框架,我的想法就是在讲每个步骤的时候讲讲自己当年在这些步骤上犯的错,实际的经验,这样我觉得也许大家以后在做系统设计时就能想起,所以我觉得系统设计这种培训绝对是需要具备大量实际经验的架构师才能做。
在运用部分,采取的方法就是通过互动,让大家按照同样的思考框架来讲自己的所负责的系统,通过互动来更好的彼此对齐,并借此逐渐变成习惯。
作为系统设计的第一步,重要性毋庸置疑,如果连系统建设的目的都没搞清楚,后面所有的步骤都会错误。如果我们去看很多的系统设计,会发现压根就没有系统建设的目的的仔细分析。
当需要做系统设计时,就意味着需要建设一套新系统,或者对原有的系统进行比较大的架构的改造升级,这一定是基于什么原因才要去做的。之所以要分析好系统建设的目的,一方面是为了避免出发点有问题,系统建设的目的应该是充分反映出解决业务层面临的挑战,或者系统用户层面面临的问题的,而不是出于个人诉求,另一方面是为了确保在后续的系统设计中能保证目的的达成。
从对系统建设的目的的分析中,是很容易看出格局和高度的,这两个词看起来非常虚,但其实很实,格局和高度通常是指所做的事影响的范围大小,例如从所在的一个小团队,到所在的大部门,到所在的大BU,到所在的大BG,到跨多BG的业务板块,到整个集团,甚至是到这个社会,这里一定要实事求是,别这里讲的是世界形势,后面的整个系统设计又完全解决不了这里所说的。
结合我自己的经历来说,在早期做HSF时,在系统建设的目的这点上是最为缺失的,也导致了自己在HSF阶段犯下了几次大的错误,例如最典型的就是HSF做动态化的那次系统架构改造,如果仔细的去分析当时做这件事的目的,就会看到这个是出于技术情怀,而不是业务端面临的业务挑战,或者说HSF的用户面临的问题,也就是之前说的出发点的问题,我觉得这是很多技术人员非常容易犯的错误,就是纯粹是出于技术诉求发动的很大动作的系统重构,我自己是在有一年有一位阿里的高管在给我做辅导时,提到首先要思考清楚为什么做某件事,并且能讲清楚原因,才明白了动机真的是非常重要的,后面做事的时候才能没那么技术化了。
因为HSF/Ali HBase的经历,到了后面做阿里容器/调度、异地多活的时候在目的这块才算是能更好的把握,能更好的去结合阿里的业务所面临的挑战来看要做的事。
大多数时候,驱动系统设计这件事的发生是其他方提出的,作为架构师,做好需求的转换,决定是否要建设新系统,或重构升级老系统,深刻地去理解系统建设的目的也非常重要,因为架构师是最终要给整个技术团队讲为什么要做的,这能够更好地让团队明白做这件事的价值和意义。
总得来说,我认为做系统设计前,一定要先对于系统建设的目的分析清楚,确保系统建设有价值和有意义,同时确保后面的整个系统设计是能让目的达成的。
在分析清楚了系统建设的目的后,到了目标这个环节,最重要的是要把前面的目的的描述,转换为可衡量的目标的描述,之所以要形成可衡量的目标,最重要的原因是为了确保最后实现的系统是达成了系统建设的目的的,相信很多人都碰到过设计出来的系统和最后落地的系统很不一致的现象,通常这都是缺乏了可衡量的目标造成的。
举两个例子说下:
第一个是2011年做容器化,建设这套系统的目的是为了应对预计会越来越大的机器成本,目标相应的制定为支撑相同的业务量,机器下降一半。
第二个是2013年做异地多活,建设这套系统的目的是为了能够让业务具备更强的抵御灾害的能力,尽管后面发现因为有了异地多活,有了更多的好处,但那些确实在系统设计之初是完全没放在建设目的里的,后面能做到纯属巧合,例如因为有了异地多活使得后面的弹性借助云资源成为了现实,因为有了异地多活,基础设施技术的演进可以更加快速,在设计之初根据目的相应制定的目标为业务能够部署在中国多个地点(地点间距离>1千公里),多个地点部署的业务都处于承接流量的状态,且流量从A点切换到B点能在30s内完成。
有了清晰的可衡量的系统建设的目标,意味着:
从目的->目标这个部分,理论上并不复杂,但也很容易漏掉,导致后面的系统设计环节出问题,关键是要形成可衡量的目标,以及相应的跟踪目标达成情况的系统。
如果要达成系统设计的可衡量的目标,到底面临了一些什么核心问题,只有明白了面临什么核心问题,才能更加明确的进行系统设计来解决这些问题。
还是用我自己的经历来讲这个话题。
最开始做HSF的时候,为什么要做HSF是比较清晰的,在可衡量的目标上也有一个大概的要支持每条上亿的服务调用,但由于当时的技术功底问题,导致了在提炼核心问题上是有很大差距的,这些也造成了后来HSF总是不断的重构、修修补补之类。所以,我从来就不认为技术功底不好的人能做好一个架构师,架构师绝对不是看到的随手画几个框那么简单,那通常只是个结果,但要合理的把框画出来是需要基于非常坚实的技术功底,HSF在最初设计时认为的核心问题就是怎么实现一个易用有服务定义的RPC框架,但对于如何支撑好上亿的交互调用量,服务化上线后给业务研发会带来什么问题(例如排查问题变复杂了),在核心问题上是有很大的缺失的,例如HSF上线后才发现的中间的负载均衡的问题,而这个问题是导致了HSF结构重新设计的,这个后来回头看就会发现如果是一个知识面更广的架构师可能一开始就会想到这个核心问题,所以如果回过头去看,HSF这样的框架,要达成目标,要解决的核心问题应该是:
在做T4(容器)的时候,目的、目标都还比较清晰,问题的提炼现在回顾也做的还ok,T4要解的核心问题为如何实现在一台机器上跑20个应用,T4出现的问题更多是对于核心问题的设计方案上,这个到下篇讲围绕核心问题的系统设计上再写。
到了做异地多活的时候,目的、目标的清晰化都ok,对于异地多活而言,要做到在中国多个城市都可同时支撑流量,并且可在几十秒内完成流量切换,异地多活中物理距离所带来的网络延时是不可突破的,怎么做到多地活且流量可动态切换,要做到这个,面临的核心问题是:
到了最近几年做统一调度的时候,整个做系统设计的思考框架我觉得算是比较熟练了,所以统一调度的目的、目标都很清晰,结合当时的情况,要实现统一调度的目标,其面临的核心问题是:
从上面的这些cases来看,可以看到,从可衡量的目标映射到技术层面要去解决的核心问题,是很需要技术功底的,对于工程类型的项目、产品而言,工程经验在这个时候也会特别重要,而通常我也觉得这是衡量一个优秀架构师很直接的地方。
继前面的系统建设的目的、可衡量的目标,达成目标的核心问题后,进入到解决核心问题的设计环节了,技术人员其实最擅长的是直奔这个主题,而且估计更期盼的也是这篇,有些时候会导致跳过前面的目的、目标环节,导致最终做出来的系统要么没贴合业务挑战,要么嘛偏离了做这个系统的初衷,所以我仍然强烈建议做系统设计的同学不要着急,一步一步来。
继续结合自己的cases来讲讲解决核心问题的设计这个环节,回顾自己的cases,犯了不少的错误,也碰到了非常多复杂的权衡选择的状况,才逐渐更加明白一个架构师应该具备的一些能力。
HSF的设计
HSF在设计之初要解决的第一个核心问题就是做一个易用,能支撑每天上亿次服务调用的服务方式的RPC框架。
易用这点在第一个版本犯了错,不过还好是第一个版本,否则纠正错误的代价会无比巨大,那个版本里,如果要把一个spring的bean发布为HSF服务,或者调用一个HSF服务,需要写一个文件,在文件里描述发布的服务和调用的服务,并且在这个文件放在jboss的某个目录里,这个方式看起来对在写代码的过程中完全没有侵入,但导致的巨大问题是这文件放在哪里写,写完后部署的阶段怎么自动放到对应的目录去,在第二个版本里才把这个调整为用一个Spring Bean的方式来做服务的发布和调用,尽管这一定程度导致了业务代码需要有对HSF的明显的依赖,但对维护、部署等都变的很标准,所以从这里可以看到,设计是全方位的,要考虑到的不仅仅是怎么实现,还有别人怎么用,运行、维护阶段又是怎么样的。
HSF犯的第二个错,就是在能支撑每天上亿次服务调用的RPC框架这点上,是给我自己代码生涯最大的教训,甚至彻底改变了我之后做设计时的技术选型风格。在做HSF之前,我从来没做过一天访问量超过100w的系统,完全搞不清一个每天上亿次的系统到底有什么不同,HSF最早的版本在通讯框架上选择了JBoss-Remoting,原因也其实比较简单,因为我们用的Web容器是JBoss,结果这个版本在一个非常重要的系统上线时,出现了严重的故障,导致了整个网站的响应速度都变的很慢。当时查了几乎整整一天都没查出原因到底是什么,后来回滚恢复,所以可以肯定是HSF上线造成的,等到回滚后的一个星期内才查出原因,是因为JBoss-Remoting在调用远端时,默认的超时时间为60s,而我们后端的那个系统在处理某些服务的时候会特别慢,进而导致了共用的处理线程池满了,所以整个网站的表现就变慢了。
这次问题让我彻底明白了访问量大的系统最重要的是对整个系统的处理过程要非常的清楚,因为在访问量大的情况下,一些小的问题有可能会放大成很大的问题,进而到故障,所以访问量大的系统对技术的可控性要求是极高的,这也是最大的不同,可控性并不代表一定要完全自己写,但要求如果用到开源的东西,要对开源的东西的代码逻辑非常熟悉,为了解决上面的问题,HSF基于Mina写了一个自己版本的通讯框架,自己来处理连接方式、线程池等,后面在做各种HSF改造,以及其他技术改造时,基本都遵循了技术可控性这个原则。
在前面核心问题那篇里也讲到,HSF在设计时其实核心问题提炼的就是有问题的,导致了后面在负载均衡、服务化后问题排查这两点上出现了严重的返工现象,而这些本其实都可以避免,就像现在再去做服务化框架的人基本都不会犯这样的错了。
在负载均衡这点上,在早期版本里,是通过硬件负载均衡设备来做的,这里造成了好几个问题,一是需要先配置要调用的服务的vip地址,当然,这可以通过一个中央的配置服务器之类的方式,第二个是HSF采用的是长连接的方式,通过vip去连接后端的一个集群时,这里会出现非常麻烦的问题,例如后端集群发布重启,很有可能就会造成连接的极度不均衡,进而导致故障。
除了上面两个问题后,还有一个触发HSF去做改造的原因是当时的硬件负载设备出现了流量跑满的现象,而这是必须要经过的一个点,会造成全站全部崩溃,不希望在未来系统中有个这么大的高风险的集中点,再加上上面的两个问题,决定做彻底的改造,于是HSF开始设计了目前看起来在服务框架体系中非常经典的软件方式的服务注册、发现和寻址的结构。
在负载均衡这件事上,现在回顾也可以看出这个仍然是当初对一个访问量巨大的系统考虑不够全面造成的。
在服务化后会带来的排查问题这点上,当初设计的时候更是完全没有考虑到,导致了后面排查问题效率低、人力投入大等等问题,后来为了解决这个问题,学习了Google家Dapper的思想,但花了很长时间这东西才真正落地。
除了上面这些外,HSF其实还有各种设计问题,例如最早的通讯协议里竟然是没有版本号的,导致后面升级时处理兼容的复杂,又例如更麻烦的一个话题就是在多语言支持上。
HSF作为我第一个真正做的访问量巨大且核心的系统的设计,由于当初的技术功底,犯下了无数错误,导致了N次返工、故障和弥补,当然也让自己得到了很大的成长,这几年回过头想这个问题,越来越觉得必须无比感谢我当时的主管对我巨大的包容和支持,HSF的经历,让我在解决核心问题的设计这个环节上,明白的是作为一个架构师,在技术选型上深厚的技术功底,在整个设计方案上知识的广度,考虑的全面性(从开发态、部署态、运行态和运维态)都是要求极高的。
T4的设计
T4在核心问题的提炼上没有太大的问题,但在怎么解这个问题的设计上那犯下的错误现在来看都是低级到不行。
为了做到在一台机器上能比以前用虚拟机的方式运行更多的应用进程,最早我们采用的方法是各种hack,其实要实现的就是进程级隔离,结果就是hack到了一定程度后,确实勉强能用了,但上线了一些小范围,有了一些用户后,发现我们的hack是很难枚举的,非常痛苦,直到有一天“发现”了LXC,才走对了路线。
除了上面这个选型层面的问题外,T4的过程中还碰到过很多类似的问题,例如用什么方法去控制磁盘空间的限制,最早我们也是用的同样的image的方式,但image的方式对磁盘空间超卖其实是非常不友好的,后来为了把这个方案更换成dir quota的方案,一帮人几乎是连续折腾了一个多月,因为线上已经在运行的要通过cp文件等方法来弄。
HSF的那段看到的很多是在技术深度上的问题,而T4的这段设计,现在回顾最主要的问题是这个技术领域视野的严重问题,所以我认为作为架构师,在相应的技术领域要有足够的视野,一定要知道这个领域的工程界、学术界是什么情况,这样对自己在结合目的、目标以及一些约束条件下做出更合理的技术选型是非常重要的,之前也写过一篇关于如何扩充技术视野的文章。
异地多活的设计
到了做异地多活这个阶段,也许是因为有了前面的一些积累,总结反思,我自己觉得异地多活的设计更多的是选择,至于对错我总体认为还好,所以这里我就讲一些异地多活设计上为了解决核心问题所面临的一些权衡选择,而这也是架构师在做设计上非常重要的一个部分,如何去根据各种约束来做一些方案的权衡选择。
异地多活在核心问题上要解决的是请求封闭、数据一致性这两个关键问题,在为了解决这两个问题的设计上,参考了工程界的一些情况,最后发现我们所面临的状况还是很不一样。
在这里就抛出一些异地多活设计上所面临的选择,我就不去讲我的选择逻辑之类的了,方便大家思考,以及交流探讨。
最后根据目的、可衡量的目标、核心问题提炼、解决核心问题的设计这些环节,总结提炼下我觉得架构师需要具备的能力:
所以架构师我觉得绝对不是烂大街的头衔,要做到一个合格的架构师还是相当难的,尤其是工程类型的架构师,需要长期的实战、经验积累。
系统设计一直是我认为最难讲的内容,主要还是因为我在内部尝试做的一个系统设计的培训,非常感谢一帮同学支持了我做这个培训,要不是他们的参与,我觉得不可能写这篇文章,也不可能较为体系化的说说系统设计,并且更重要的是让我觉得系统设计这个东西其实还是可以不讲的那么虚的,以及系统设计的技能一定程度上确实也是可以培养的。
本文为云栖社区原创内容,未经允许不得转载。