余额宝架构变迁倒逼出来的大规模服务化及中台建设
The following article comes from 码码的土狼 Author 土狼
作者介绍
李鑫,天弘基金移动平台团队任技术总监兼首席架构师,主要负责天弘移动直销平台的整体技术架构和技术团队管理;在此之前,在华为的中间件技术团队,任六级技术专家,主导了多款华为软件的云计算产品的规划、设计、构建及落地工作,包括APaaS、ASPaaS、服务治理平台、分布式服务调测框架等几款产品;更早之前,在当当网的运作产品中心做技术负责人,主要负责电商中后台的仓储、物流、客服等系统的重构优化及技术管理工作。
这次的分享首先介绍余额宝业务及架构的变迁历史,接着介绍天弘基金是如何对自有的基金销售系统进行服务化的改造,及由此引申出的业务中台建设实践,最后介绍服务化带来的冲击及影响,以及我们是如何进行服务治理、数据治理及团队协同的治理。
余额宝算是中国第一支真正意义上的普惠金融产品,它本质上是一支货币基金,背后是天弘的增利宝基金。由于与支付宝的关系,它既是理财产品,又是支付产品,理财和支付的双重属性,再加上互联网产品所特有的极简的用户体验,让余额宝一经推出,迅速成为 “爆款”,短短六年获得了惊人的发展。毫不夸张的说,余额宝的出现,开启了中国的全民理财时代!
为了适应业务的爆炸式增长,余额宝的技术架构前后共做了4次大的变更,每次技术变更的背后,都承载了天弘技术团队对系统规模、可扩展性及升级成本的综合考量和平衡之道。
余额宝上线之初,解决的是“从无到有”的问题。
当时没有其它类似的互联网金融产品可以做参考,我们采用了传统的典型企业级金融架构。采用自建IDC机房的方式,用硬件负载均衡设备将交易请求接入进来,前端是由六台weblogic构成的小型前置处理集群;请求被稍做处理后,即被推送到后端由金正的基金交易中间件构建的集群做业务处理,其中 KCXP 和 KCBP 分别是金正公司的基金交易消息中间件和业务中间件;业务数据存储采用的是两台Oracle数据库服务器构建的一主一备小集群,这个是线上库,为了缓解数据存储压力,历史交易记录会被定期抽取到一个同样架构的历史库服务集群。硬件方面采用的都是惠普小型机设备,数据备份则采用EMC的产品。
这是典型的IOE架构,全套的商用软硬件配置,成本很高。架构体系里的每一层虽然都采用了集群的方式,但更多的是使用主备模式,而不是负载均衡的分布式模式,因此系统的单点资源负载压力较大,尤其是数据库,基本上就是单库模式(另一个库是备库),扩展性较差。
当时的系统容量设计上限是能支持千万级用户。传统基金销售模式是走代销机构,投资基金用户也多以理财为目的,所以每天可能处理的帐户开户也就是几万到几十万的规模。由于余额宝对接的是支付宝,支付宝的用户规模达到千万级,这是设计产品时对需求的定位。当时预估,这个设计容量怎么都能扛个几年。结果,上线短短10天左右,新开户数已经突破100W了,按这个速度,3个月左右数据库容量上限就要被突破!事实也是如此,我们三个月就达到一千万的客户,余额宝一期系统的生命周期只有三个月!
所以一期系统甫一上线,团队就不得不开始考虑扩容,计划将容量扩充30~50倍,计算规模也同步增加。如果还是采用一期系统的纯商用软硬件的模式进行横向扩展的话,成本要达到1~2个亿,这样的成本对于当时天弘这样一家小型基金公司而言,是不可负担之重!因此,在阿里的建议下,我们决定整体上云。
做出这个决定的压力是非常大的,因为当时没有任何一家金融公司和基金公司会将他们的核心交易系统构建在云上。但在巨大的成本压力之下,我们走出了这一步,成为那个第一个吃螃蟹的人!经过紧张的迁移工作,我们将系统整体搬迁到阿里云上,这就有了余额宝2.0架构!
2.0的架构中用阿里云上的软件负载均衡SLB替换了硬件负载均衡;用阿里云上的虚拟计算单元ECS替换小型机成为前置服务及中间件服务的服务器;用阿里云的web服务替换了前置服务器上的weblogic。同时将服务集群的数量从十几个节点扩充到近60个节点。
最大的变化是数据库,原来的Oracle单库被替换成了阿里云上的一个50个节点的RDS集群,实际上就是进行了分库分表的拆分。经过测算,需要使用50组业务节点,但在拆分时,考虑到扩展性,并未简单地拆分成50份,而是根据用户帐号ID作为分片主键将所有的核心业务表,包括资产、账户、交易等都各拆分成1000份,然后每个节点处理20份数据(物理子表)。这样做的好处是将来如果系统遇到瓶颈,需要扩容时,不需要对拆分算法进行修改,而且数据平均迁移时只需要以库为级别进行,从而避免了拆表。
上云后,不仅数据库总容量有了几十倍的提升,交易处理效率也从一期的120W笔/小时提升到了近2000W笔/小时;最大清算时间从之前的7个多小时降低到不到2个小时;而总成本,才增加了不到1倍。所以,上云对我们系统整体效率的提升和成本的降低有非常明显的作用。
余额宝2.0的架构奠定了余额宝后续架构的基调,它在线上稳定运行了近3年,中间经历了几次小的升级优化,有力的支撑了这个过程中的各种业务创新,但也在支撑业务快速发展中埋下了一些“坑”。
2016年,我们决定对系统进行一次大升级。这次升级的主要基调就是“业务逻辑重构,优化清算流程”,这就是余额宝3.0!
这一阶段的业务逻辑复杂度要远远高于3年前,因此3.0的架构中,我们首先对服务按直销和代销业务进行了拆分,并在拆分后,将服务节点从原来的60台扩充到了340台。由于数据库前期预留的buffer比较大,并没有成为本阶段的瓶颈,依然维持了50个节点的规模。
这次升级之后,总体计算能力有了较大幅度的提高。处理效率比起2.0阶段增长了2倍多,也支撑了2016年春节期间日交易4亿多笔的峰值。但是,清算时间比2.0时期有较大增长,这成了系统的一个“隐患”。
2017年,为了配合支付宝拓宽线下支付场景,并将业务推广覆盖到三、四线城市,同时,也要解决清算时间过长的隐患,我们规划了余额宝4.0的升级。
这一阶段的系统规模已经很大了,如果还是按3.0的架构进行横向扩充的话,成本将呈线性增长。经过综合考量,决定在升级前,先优化单节点的处理性能,提高效率及负载后再扩容,以降低总体的升级成本。
因此,这次升级首先将直销和代销业务合二为一,同一台计算单元既处理直销业务,也处理代销业务,提高了单点的处理效率。在此基础上,再进行扩容,将计算单元从340节点扩充到480节点,数据库扩容4倍。通过这两步优化及扩充动作,最终系统的容量及计算能力均有了4~8倍的提高,这套新的架构支撑我们平稳度过了2018年双十一及春节的交易高峰。
看到这里,大家可能会有疑问:一般在系统服务化改造中,服务是会被拆的越来越细,为什么我们反其道而行,反而对服务进行了整合?这是因为,余额宝发展到这个阶段,业务模式已经比较成熟,通过细粒度的服务模态来保证可扩展性的需求已经不是那么强烈;另一方面,系统规模庞大,扩容的成本成为重点考虑因素;综合这两方面的考量,适当增加单点的复杂度,可以降低整体成本。
目前,针对余额宝这单只基金已经建立起了一套完整的技术生态体系,它的核心是余额宝的产品、帐号、交易、清算等模块,在结合实时调用和文件交互两套接口的基础上,构建了电商大数据的分析体系及一系列辅助支撑系统。同时,余额宝系统和其它第三方系统也有大量的交互,包括支付宝、监管、直代销渠道等等。
余额宝系统的建设直接锻炼了天弘的技术团队,让我们明白了大型互联网应用是什么样的玩法,这也直接推动了天弘基金销售平台的整体服务化改造。接下来,我将详细介绍基金实时交易平台的服务化改造。
在开始这部分内容之前,先简单的介绍一下基金公司的业务。
基金公司最核心的业务就两块,一个是基金的交易,另一个是投资。我们从直销和代销渠道把交易请求“接”进来,在我们的核心交易系统进行申购、认购、定投、赎回、转换等等操作,这个过程里,会涉及与支付渠道、银行等一系列的交付。同时,还有大量的清结算和TA清算,这个过程里,要与银证监会、中登等一系列监管机构有数据上的交互。聚拢过来的巨额资金会被统一管控,并投入到股市、债市、货币市场等投资市场去赚取收益。围绕这个业务还有相应的投研、基金产品管理、风控、客服等中后台的业务支持。
以上,就是基金公司的日常业务模式。
在天弘早期的基金销售系统建设中,其实没有什么服务化的概念,当时常用的做法是有什么类型的业务,就针对这种业务单独开发一套独立的销售及清结算系统,存储也是各用各的。由于业务量普遍不大,这些系统往往采用单体架构模式,不考虑横向扩展性。经过多年的发展和积累,内部多套直销及代销交易系统并存,系统间帐号没有打通,用户的资产数据无法统一,用户体验差;另一方面,各系统间功能重复的现象严重,不仅重复占用软硬件资源,版本的控制也很麻烦。
这种状况甚至在我们整体迁移到云上之后还存在了很长一段时间。因此,所谓的“服务化”,并不仅仅是上云那么简单,它还涉及到架构思维的转变。
痛定思痛之后,我们决定对系统进行服务化改造。将这些系统中通用的能力,包括用户账户、交易、支付、资产、结算、权益、投顾等功能抽取出来,改造成服务后,以服务集群的形式独立部署,统一对上层应用提供通用服务,这就是我们内部俗称的8大服务中心,它们共同构成了后台的通用服务层。
原来的业务系统拆分之后变得更轻了,更多的充当了一个交易大厅的角色,我们称之为前台服务层。我们的网关层共提供了两套网关,一套是SLB,另外一套是移动网关,用户的交易请求通过网关层被统一接入进来,先在前台服务层进行协议解析、安全校验及简单的业务逻辑处理后,再通过调用后台通用服务层的服务进行基金的相关交易。
由于我们早期前端的业务渠道不多,直销只有APP及官网销售两个渠道,代销只有余额宝和基金淘宝店,业务比较单一,这样的两层服务化拆分已经够用了,前台服务层的粒度已经足以支撑正常的业务迭代速度。
但是业务很快进入一个快速发展的阶段,这个时期有大量的终端渠道要接入,除了早期的销售渠道之外,还增加了微信、钉钉及其它多家第三方的销售及运营渠道,业务迭代的速度也明显加快。
举个例子,我们有一个叫“目标赢”的定投产品,在很多终端渠道上进行销售,每个渠道对这个产品的销售或多或少都有一些个性化诉求,有的在客户校验上要增加特殊要求,有的要求在产品销售上叠加一些额外的运营规则。
综合来看,这些终端渠道对“目标赢”这个产品的功能诉求中,有80%是一致的,只有20%的个性化要求,如果还是按早期的两层服务划分方式,我们需要为每个渠道都开发一个重复度很高的前台服务,这明显不划算,太重了。
怎么办呢?继续拆!将“目标赢”这个能力中,和渠道无关的80%通用业务功能拆出来,继续下沉,形成一个新的服务分层,我们称其为业务服务层;这样还不够,我们继续将前台服务层中剩下的20%个性化能力中和业务无关的功能,包括协议适配、拆包、安全校验这些功能也拆出来,和网关层合并,形成了安全网关这个新的扩展。
这样,前台服务层只剩下10%左右的业务功能了,而且里面大部分的逻辑是对业务服务层及通用服务层的能力进行组装和聚合,这个时候,前台服务层已经被拆的很轻了,我们改称其为聚合服务层。通过这种继续拆分的方式,我们将早期两层的服务化分层架构,演进成包含聚合服务层、业务服务层、通用服务层的三层服务化分层架构。
17年之后,“业务中台”这个概念大火,我们重新审视我们的服务化架构,发现业务服务层这个新的服务分层和业务中台定义高度匹配,把它定位为基金销售业务的业务中台毫无“违和感”。因此,聚合服务层、业务服务层、通用服务层这样的三层分层的定义换个说法就是业务前台、业务中台、通用后台。这样的分层架构也并非我们刻意为之,而是基于业务的驱动,在服务化的架构下自然形成的。
目前,这整套服务化平台都是在阿里云及蚂蚁金融云的IaaS层及PaaS层能力基础上构建的,我们在它们的基础之上还构建了相应的日志监控、服务治理、APM动态调用链跟踪、运维管控等一系列能力。
以上,就是我们目前基金实时交易平台的整体服务化的架构。
将我们线上系统架构变迁的整个历史拉通来看,可以看到经历了3个阶段。
早期,就是典型的单体+竖井架构,复用性及可扩展性都差。
在服务化的初期,将系统进行了两层服务化拆分:前台业务服务层+后台通用服务层。在业务发展比较平稳、前端业务渠道不多的时候,两层服务化分层已经足以支撑业务的正常迭代。
随着业务的快速发展及多端适配需求增多,两层架构下的前台业务服务层还是太重,无法支撑业务的快速变更,这时候将其继续拆分,将业务域的通用服务继续下层出新的业务中台层,这就形成了现在的三层服务化分层架构。
在目前的三层服务分层架构下,通用服务被不断下沉,所以越底层的服务被抽象度越高,也更通用、更静态,不会经常改变;越靠近前端的服务越贴近业务,越不稳定,会随着业务的快速变化而不断改变,客观上也必须保持更轻的体态。
基于我们系统服务化的经验,谈谈我个人对业务中台这个概念的理解。
所谓“业务中台”,本质上属于服务分层的概念,因为既然有中台,架构设计上就一定存在多层,一定存在前台和后台,脱离前台和后台谈中台没有任何意义。中台这个概念大概在2017年的时候由阿里正式提出来,之前基本上没有人提这个概念,大家更多是在谈服务化和服务分层,实际上它不是一个新的东西,而是对“现有概念”的重新包装,新瓶装旧酒而已。这个概念的本质就是“服务化+服务分层”,它只是服务化分层中的一层而已。
为什么会形成业务中台这样一个服务层呢?本质上还是和业务的发展息息相关,业务的快速发展会驱使你不断的对服务进行拆分,并将通用服务不断下沉,只有这样,才能将服务拆的更小,而服务粒度越小,可组装性越好。只有这样,我们才可以根据不同的业务形态,通过服务组装和聚合的方式快速构建出前端应用,从而实现更快的开发速度,以支持业务的快速迭代及试错。
如果你的业务形态比较单一,发展相对静态的话,就不会有那么强烈的服务分层和拆分的需求,自然就长不出所谓的“业务中台”的服务分层。所以,业务中台的建设要顺其自然,强扭的瓜不甜,企业需要根据自己的业务特征来判断是否需要建设业务中台。
业务中台的存在解决了前台业务服务和后台通用服务之间的适配问题,让前台服务不需要通过大量的代码来处理业务逻辑,只需要通过少量的粘合剂代码来对中台专属业务领域的通用服务和后台跨业务领域的通用服务进行快速组装及聚合,这就从根本上降低了前台服务开发的工作量和成本。业务变化越快,多端适配的需求越多,中台建设的收益越大;如果业务比较平稳,也没那么多的业务渠道,那么中台建设的意义就不大了,像我们早期所采用的前后两层的服务化架构就能工作得很好。
既然业务中台本质上是一个服务分层,那么服务化架构的分层质量对业务中台的形成就有很重要的意义。如何进行更好、边界更清晰的服务分层,会直接影响到能否成功长出业务中台这个分层形态。
讨论这个问题之前,先举一个我自己的例子。我目前虽然是负责技术团队的管理,但每隔一段时间,都会给自己分派一些迭代的开发工作,以免离一线开发太远(^_^)。前段时间,我负责了一个叫理财月报的功能模块的开发,同时在这个项目里,还挑战了一把“全栈”:前端H5和后端的Java服务开发都由我一个人负责。在我们团队规范中,严格规定了前端应用必须尽量少的承载业务逻辑。刚开始我还严格执行这一规定,但在迭代中间,产品提了一些功能变更,分析后,发现服务端修改比较麻烦,我当时“懒”了一下,直接就在前端通过JavaScript把逻辑给写了。迭代结束后,重新审视这个功能模块,虽然能力实现了,但架构规范并没有贯彻的很好,前端还是多少承载了一些本不必要的逻辑处理能力。
我举这个例子的目的就是要说明,如果由一个团队(或者一个人),同时负责多个架构分层的能力开发的话,很容易由于惰性或者自我妥协的情绪而导致分层边界混淆不清,最终长出来的,很可能就是一大坨互相耦合在一起的服务,更别谈清晰的层次划分了。所以,我们的后台通用服务、中台业务服务和前台服务都分别由不同团队负责,大家负责的能力之间存在清晰的划分边界,共同遵循严格的服务接口契约。这也符合康威定律:组织的协同及管理模式必须与所采用的系统架构相匹配。
总结来说,要最终推动业务中台这样的服务分层的诞生,业务驱动、服务分层、通用服务下沉、组织拆分都是必不可少的因素。
以上就是我们在服务化以及业务中台建设方面的一些实践经验。接下来,我来讲讲服务化对我们整个研发体系所造成的冲击、带来了哪些困难,以及我们是如何应对的。
我们走上服务化这条道路的诉求非常简单直接,就是要解决单体及竖井架构所带来的系统可扩展性及可维护性的问题。我们曾经以为,只要引入服务化,这些问题都能迎刃而解,从此就能走上研发的康庄大道^_^。
但当真正踏上这条道路时,才发现,走上的压根儿就不是一条快车道,而是一条烂泥地,遍地都是坑。我们确实解决了一些问题,但同时也遇到了一系列新的问题。
服务化的本质就是一个“拆”字,原来的单体应用被拆成了大大小小的应用集群和服务集群,并被分散到网络的各个节点,由不同的团队负责,每个团队各管一段。
在这个离散化的大背景下,运维和研发都会遭遇一系列新的问题和挑战,包括集群环境中故障的定界定位:你如何快速定位问题出在哪?整个集群的调用关系是什么样的,如何梳理?合理不合理?整个集群的性能瓶颈在哪?这些都是运维要直面的问题。对研发来说,挑战也不少。团队被拆分了,团队之间如何高效的协作?系统被拆分了,功能被分散到远程节点,如何做调试?分布式架构下的数据一致性如何保障?等等…
所以,架构的变更带来的影响是全方位的,影响的绝不仅仅只是应用系统,它会对整个研发体系,包括开发、运维、团队组织、协同都带来冲击,你必须构建起一整套从线下到线上的新的能力体系来支撑这套新的架构。很多团队没能构建起这套能力体系,直接在服务化的“反噬”下,倒在了路上,看不到服务化带来的“曙光”。
下面来看看,我们是如何破解这些困局的。
事务的一致性和可用性问题是分布式环境下“老生常谈”的问题了,相信大家也或多或少遇到过。它不仅仅是服务化的问题,而是分布式架构的一个共性问题,所以我在这里把它单独“拎”出来讲讲。
针对分布式事务,我们采取了3级应对策略。
首先,在分库分表操作中,坚持“实体组”优先的策略,尽量按照统一的“片键”进行分库分表操作。比如,如果统一按“用户账户ID”作为分库分表键的话,那么用户相关的交易、资产、支付等信息都会落到同一个物理库实例中。这样,针对此用户的相关操作,本地事务就可以生效,避免了分布式事务的使用,因为对于任何分布式事务而言,不管做不做资源锁定,为了有效保障事务状态,都需要额外的资源处理消耗。
其次,我们还提供了自研的支持多级事务的TCC服务,以应对不可避免的分布式事务需求。采用TCC的最重要原因是与其它资源管理器相比,它相对简单,我们不用关注资源层面,只需要关注服务接口即可。上面的第二张图是TCC的典型架构模式图,相信只要研究过TCC的同学们一定看过这张图,第三张则是我们自研TCC的详细的交互架构图,能体现更多技术细节,可供大家参考。从我个人的理解而言,实现一个TCC框架(独立服务)并不麻烦,最核心的是要解决两大问题。一是参与事务的业务数据的缓存和回放问题,我们在做TRY操作时,需要将事务数据缓存起来,可以存到数据库或者分布式缓存中,但不能缓存在本地,这样不可靠。当框架做Confirm和Cancel操作时,再用缓存的事务数据去运行特定的服务逻辑。这就要求在TRY、Confirm和Cancel的方法构造上要有一定的约束,相互之间要能够识别哪些入参是事务数据。另一个是父子事务的事务ID的传递问题,我们通过分布式服务框架的“非业务传参”来解决这个问题,一旦某个事务构建了一个事务ID,这个事务ID就会被放置到环境上下文中,并随着RPC调用传递到远程服务,远程服务如果侦测到上下文中已经存在事务ID了,就不再构建新的事务ID,这样的话,父子事务之间就通过同一个事务ID关联在一起。
最后,最终一致性的保障一定是“对账、对账、对账”!这是最传统,也是最可靠的最终防线,这也是金融公司的基础能力,这里我就不展开详细说了。
企业的服务化之路要走得顺畅,一定要两条腿走路,一条腿是需要一套很好的分布式服务框架;另外一条腿,就是要做服务治理。只有这两条腿都健壮,服务化之路才能顺利。
我们在服务化之后就同步开展了服务治理的工作,对服务治理这个概念的理解也随着实践经历了由浅入深的过程。
这里直接给出我们做服务治理的整体架构图。服务治理既要进行线上的治理,也要进行线下的治理,通过线上线下两大维度进行治理指标的采集,并把它汇总到数据仓库中,进行统一的度量和分析。
这些度量指标中,有相当一部分线上的性能及异常指标会被转化为运维事件,一旦触发我们预先设置的阈值,就会被进一步转化成治理“管控指令”,并通过调度中心下发,驱动云上的资源编排和调度能力,进行服务的弹性伸缩、扩容缩容操作,以应对流量的波动,或者直接驱动服务进行服务内部的限流、降级、容错、路由调整等管控操作。
另外一部分度量指标,尤其是线下度量指标,包括架构、开发、测试、运维、过程协作效率等指标会通过治理委员会(泛指,治理成员的集合)进行人为的深入分析,并制定出治理决策,这些治理决策会通过相关的管理措施进行落地。
这样,我们就通过服务的度量、管控、管理三大举措,构建起一个三位一体、围绕服务治理的闭环体系。在这套体系中,针对服务的度量是进行服务管控和管理的前提和基础,只有看得到,才能管得到,必须先解决“看”的问题,才能解决“管”的问题。接下来,我们就来重点介绍如何构建微服务的度量及分析体系。
管理学大师彼得.德鲁克说过“如果你不能度量它,你就无法改进它”,强调的就是度量的重要性。
度量的第一步,就是度量指标的采集。
前面介绍了,微服务的治理囊括了线上及线下体系,同样的,服务治理度量指标的采集也要线上线下同步进行。
在线上,可以通过服务注册中心获取到服务的注册信息及服务的管控指令信息,通过各个微服务主机节点上的主机日志、应用及服务日志、APM监控的调用链日志,可以获取到相关的性能及异常指标信息。
线下的指标就更多了,通过需求管理系统,可以采集到UserStory及各类需求的基本信息;通过项目管理系统可以采集到开发人员、开发团队、开发任务的基础信息;通过测试相关的管理系统可以采集到测试用例及测试bug的相关定义信息及过程指标信息;通过源码仓库及软件版本仓库可以采集服务之间的调用关系(代码扫描获取到的静态调用关系),以及最终研发产出物的基本信息。
软件研发是一个强调协同的群体行为,产品、开发、测试、运维需要紧密合作。为了达到更高效的配合,我们经常会采用一些协作模式,比如针对开发和测试之间的配合,采用持续集成(CI);针对产品、开发、测试的协作,采用敏捷协作模式;另外,还会使用一些DevOps的Pipeline。不管采用何种协作模式,都可以从其相关的过程管理系统中抽取出相关的过程指标事件,比如一个任务什么时候完成设计、什么时候开始进入开发、什么时候完成开发…等等,这也是一大类很重要的治理度量指标。
有了以上这些线上线下指标,就可以将它们统一汇总到数据仓库,进行进一步的深度度量和分析。
来看看最终针对微服务治理的度量及分析体系是什么样子。从下往上看,首先通过各个指标来源渠道获取到上述的各类度量指标,并把它们以ODS(操作型数据)的格式汇总到数据仓库;通过数据模型抽取出主数据(MDM),这些主数据包括了服务、需求、任务、人员、团队等,在此基础上,通过不同的数据主题(Topic)构建起一个多层的“数据集市”,这些数据主题包括了异常、性能、资源、调用关系、容量、系统、测试、开发、运维协同效率等;在这个数据集市的基础上进行各类数据分析,包括性能分析、容量分析、健康度分析、团队及个人的质量报告、质量趋势、动态调用链及静态调用链的深度梳理、以及各维度的汇总报表。根据这些分析报告,由治理委员会进行深度的分析并制定出各类的治理决策,或者通过人为或自动化的机制发出各类管控指令。
治理决策和管控指令就是微服务度量及分析体系的最终产出物。
有了治理决策和管控指令,就可以对微服务的线上及线下体系进行治理,首先来看一下对线上服务体系如何进行治理。
服务限流是微服务集群自我保护的一种常用机制,我们对线上调用比较频繁及资源占用较大的服务都加上了相应的限流举措,并构建了单机限流及集群限流两套限流措施。
首先来看一下单机限流,它有多种限流算法可供选择,最主要的是两种,漏桶算法及令牌桶算法。它们之间有什么区别呢?打个比方,比如今天这个会场的人已经爆满了,门口保安开始限制客流,一种举措是会场中出来一位听众,才放进去一位听众,这样可以保证会场中的听众总数是固定的,人人都有座位,这就是漏桶算法---必须有“漏”出去的,才能有进来的;另外一种举措是不管有没有听众出去,保安固定每隔5分钟就放一波客人进来,这和春运火车站的波段式限流非常类似,可以保证客流比较均匀,但是这种策略也有一定的风险,如果听众离开得不够及时,会场中的人总数可能会升高,导致一部分听众没有座位,这就是令牌桶算法。因此,如果要对线上并发总数进行严格限定,漏桶算法可能会更合适一些,这是单机限流机制。
单机限流下,各个服务节点负责各自机器的限流,不关注其它节点,更不关注集群总调用量。但有时候,因为后台总资源是有限的,虽然各个单节点的流量都没有超,但总流量可能会超过后台资源的总承受量,所以必须控制服务在所有节点上的总的流量,这就是集群限流。
集群限流能力的构建要更复杂一些。首先在各个微服务节点上要有一个计数器,对单位时间片内的调用进行计数,计数值会被定期的汇总到日志中心,由统计分析器进行统一汇总,算出这个时间片的总调用量。集群限流分析器拿到这个总调用量,与预先定义的限流阈值进行比对,计算出一个限流比例,这个限流比例会通过服务注册中心下发到各个服务节点上,服务节点基于限流比例各自算出当前节点对应的最终限流阈值,最后利用单机限流进行流控。
可以看到,这是一套环环相扣的、各环节紧密协作配合的技术体系。单纯拎出一个点来看,实现技术都不麻烦,但要构建起贯穿整个技术栈的技术体系,则需要有一套统一的技术标准,各个环节都遵循这套标准,并对不符合标准的应用及环节进行改造,保证标准落地,这样才能构建起完善的限流技术体系。因此,构建服务限流能力的难点,一是标准化,二是体系化。
体系化的构建是最难的,但也是最有威力的,大公司往往就是依靠体系的力量去构建起护城河,从而去碾压没有体系能力的小公司。
另外,限流一大原则是限流动作尽量前置,毕竟被限制的流量注定要被“抛弃”,越早处理越好,免得无谓的消耗资源。
集群容错是微服务集群高可用的保障,它有很多策略可供选择,包括:
快速失败(failfast)
失败转移(Failover)
失败重试(Failback)
聚合调用(Forking)
广播调用(Broadcast)
很多容错策略都是靠重试来实现的,但不管你决定使用哪种集群容错策略,一定要注意控制重试的次数,尤其是线上负载已经很高的时候,如果重试次数太多,一方面会推高服务被调用方的负载及并发,另外一方面会导致服务调用方的调用延时增长,双重因素叠加之下,最终极可能导致“服务雪崩”、集群被“击穿”。
所以,在使用集群容错的时候,一定要设置最大重试次数。另外,有可能的话,尽量做“重试减速”,也就是让每次重试的间隔时间越来越长(可以考虑采用指数),这样可以将重试带来的压力尽量分散到一个足够长的时间段,避免阻塞在一起造成人为的DDOS攻击。
服务降级和服务限流类似,也是微服务集群自我保护的机制。一般在线上动用服务降级手段时,都是线上比较危急的时候,生死存亡了,这时候留给你思考和反应的时间不多,所以在使用服务降级之前一定要做好预案,提前梳理出核心业务链路和非核心业务链路,然后通过降级开关一键把部分或所有非核心链路降级,这样才能“救命”。
服务降级也有很多手段可以使用,包括:
容错降级
静态返回值降级
Mock降级
备用服务降级
我们常说的熔断,本质上也是容错降级策略的一种。相比一般容错降级,熔断提供了更为丰富的容错托底策略,支持半开降级及全开降级模式。
构建服务降级能力也和限流机制类似,同样需要坚持标准化和体系化。
线上的流量今天涨一点,明天涨一点,如果麻痹大意,或者监控手段不到位,说不定哪天服务就被冲垮了,所以要对线上的流量时时进行规划,以做到心里有数。
容量规划有两种形式,一种是容量预估,另一种是性能压测。
系统或者服务上线之前,首要进行容量的预估,一般做法是基于经验,同时结合对业务的前景预期,先估算出一个总的调用量,比如1亿的PV,可能会有10%的流量落在购物车服务上,购物车服务就是1000万的PV,一次购物车访问会产生2次数据库调用,那么它的关联数据库就会有2000万的调用量。这样,基于图上从左至右,层层分解,可以获取到每个服务节点上摊到的访问量,再结合运维部门的单机容量指标,就能估算出整个集群需要多少的软硬件资源。
系统或者服务一旦上线,它的性能就开始处于不断“劣化”的状态,上线前预估的指标会越来越不准,这时候就需要通过线上性能压测来对实时容量进行监控。做线上性能压测也要遵循一定的规律,不是说一上来就做全链路压测。同样是基于上图中的调用关系,线上性能压测首先需要在调用链的末梢,也就是对数据库或者缓存先进行压测,以保证它们不是瓶颈,再对调用数据库或者缓存的上一级服务节点进行压测,然后一级一级往上压测,最终覆盖整个链路,实现全链路压测。
可见,全链路压测的前提是单点压测,需要先把单点压测能力做好,才能做全链路压测。
在压测的时候,由于流量是模拟的,数据也是“伪造”的,所以一定要做好隔离,各种各样的隔离,尤其是数据的隔离。我个人不建议将“染色”的压测数据和真实的线上业务数据共表存储,最好将“染色”数据单独表存储,并通过分表策略进行区隔。
另外,额外说说自动化压测这个事。古希腊哲学家赫拉克利特曾经说过:"人不能两次踏进同一条河流"。随着时间的流逝,任何事物的状态都会发生变化。线上系统也同样如此,不同时刻的同一系统的状况也绝对不会一模一样。所以每次性能压测,都可能会遇到一些突发状况,尤其是在负载特别高的极限状态下,甚至会遇到一些以前从未出现的状况。压测系统虽然做了数据及流量隔离,但所有的预防策略都是基于已知的知识结构来构建的,它不一定能防控住这些新状况,乐观一点,就算你控住了99%的突发状况,只要漏掉1%,就可能导致整个线上服务失控。这时就需要人工介入来判断,并有针对性的进行处理。所以,全自动化、完全无需人工介入的压测在我看来还不太现实。从我的经验来说,在线性能压测是个“苦活”,需要提前发公告、全员戒备、时刻关注监控指标、有时候还要通宵搞,总结起来就是:智力密集型+劳动密集型+人肉密集型。
以上就是性能规划,包含了容量预估与性能压测两大能力。
线上的故障定界定位应该是我们每天做的最多的事情。分布式环境下的故障定界定位,最有效的工具就是动态调用链路跟踪,这应该是没有疑义的,不管你是使用开源的Zipkin,SkyWalking、PinPoint、CAT,还是使用商用的听云、AppDynamic或NewRelic等等。
调用链本质上也是基于日志,只不过它比常规的日志更重视日志之间的关系。在一个请求刚发起时,调用链会赋予它一个跟踪号(traceID),这个跟踪号会随着请求穿越不同的网络节点,并随着日志落盘。日志被收集后,可以根据traceID来对日志做聚合,找到所有的关联日志,并按顺序排序,构建出这个请求跨网络的调用链,它能详细描述请求的整个生命周期的状况。
动态调用链要用的好一定是需要和监控大盘相结合。介绍一下我们的使用经验,我们在很早之前就根据Google的那篇Dapper论文构建了动态调用链跟踪体系,在我们的监控大盘上有很多的点可以进入调用链:
1)我们有一个单位时间段内异常最多服务的TopN排序列表,点击列表上的任何一个服务,会打开这个服务这个时间段内所有异常的列表,再点击列表上的每一个异常,就会打开这个异常所属调用链,进行故障分析。
2)可以利用监控大盘,监控大盘上有很多“毛刺”,这些都是系统的一些异常点,点击任何一个“毛刺”,会将“毛刺”所在时间段内的请求以“散点”的形式列出(可能会基于请求数量做抽样),“散点”的颜色代表了不同的状态,有的成功,有的失败。点击任何一个“散点”,就可以进入这个请求对应的调用链。
3)针对核心服务的异常有专门的监控表格,会列出最近发生的核心链路服务上的异常,点击这上面的任何一个异常,也可以进入对应的调用链。
以上就是基于动态调用链进行线上故障定界定位的常用模式。
我们目前使用蚂蚁金融云提供的SOFA分布式服务框架,针对服务的线上生命周期管控的能力也是基于蚂蚁金融云的能力来构建的,蚂蚁金融云在它的云上弹性计算资源的基础上,通过整合资源编排及资源调度的能力,构建了微服务的综合管控平台,通过这个平台,可以比较方便的进行服务的上线、下线、扩容、缩容等操作。
我们将云下的研发流水线和云上的服务管控能力进行了打通,实现了从线下的产品设计、服务应用开发、服务应用构建,到云上的服务应用部署、监控与运维的一体化联动。
由于我们是做金融的,属于国家强监管行业,安全合规在研发工作中占了很大的比重,所以研发流水线中增加了很多审核及安全校验的环节,这也客观导致了整条流水线会比较重,这是我们和一般互联网公司的区别。
在传统企业级开发中,一般遵循先架构设计再开发的协同联动原则,但在服务化架构下,由于原来的单体系统被拆的很散,传统架构师的职责更多的被分散到一线的研发团队,由开发人员来承担。看起来,貌似架构师的职责被减弱了,而且由于服务的粒度比一般应用小,架构的工作好像没有以前那么重要了。
实际恰恰相反,在离散化体系中,更要加强对架构的管控,以防止整体架构的劣化。与传统的“架构先行”模式不同的是,服务化下的相当一部分架构工作被“后置”了,成了一种事后行为,也就是说,架构度量和优化的工作会大幅度上升。这里我们就主要讨论如何通过服务之间的调用关系来针对服务化的整体架构进行度量和治理。
上图是线上服务集群服务间的调用关系总图,它可以通过动态调用链的汇总来获取,目前大部分公司都是这么干的。但使用动态调用链有个很大的缺陷,即调用链只有在运行时才能生效,而且,必须是有埋点并实际发生的调用才能被监控和采集数据,不管这个埋点是手工埋点还是自动埋点。而对于一个复杂的平台或大系统而言,存在大量的冗余分支和异常处理逻辑,这些分支及逻辑,往往需要在特定场景下才会被触发,甚至可能永远都不会被触发,所以通过动态调用链抓取的这个服务之间的调用关系是不完整的。为此,我们除了使用动态调用链,还开发了静态代码扫描技术,通过代码之间的调用关系来生成这个调用关系图,通过这种方式可以有效弥补动态调用链的不足。
有了微服务间的整体调用关系之后,我们就可以对微服务的调用质量进行深入的分析,所有的分析都建立在这个图形的基础上,因此我们会使用图论的相关算法。
服务是分层的,好的服务调用关系一定也是分层的,层层往下推进,最终形成一个有向无环图(DAG)。因此,可以对调用关系图进行闭环检测,如果检测到如图上G点到B点这样的回环调用的话,说明调用关系有问题,需要进行优化。这种回环调用也许现在无感,但难保未来哪天就会由于一条旁路逻辑导致死循环。
另外,还可以对整个调用网络进行遍历计算,找出所有调用深度最深的调用链,如图上红色标注出来的调用链。我们知道,对跨网络的调用访问,涉及到的网络节点越多,稳定性越差。因此,可以将所有调用链路最深的这些链路找出来,并按调用深度进行topN排序,重点对排在头部的调用链分析它的必要性及合理性,看是否可以对调用深度进行缩减和优化。还可以找出整个网络中被调用最多的服务节点,比如图上的F节点。从调用关系上来说,它是被依赖最多的节点,自然是最重要的节点,作为枢纽节点,在运维等级上需要重点保障。当然,实际应用中,我们还会加上调用量这个权重来综合判定服务节点的重要性。
随着架构的不断演进,可能有些服务节点再也不会有调用关系了,比如图上绿色的L节点,这些节点再不会去调用别的服务节点,别的服务节点也不会来调用它。这类被找出来的“孤零零”的节点,可以考虑对它进行下线处理,以释放资源。
以上所有的度量和治理都是在这个调用关系图的基础上进行的,所用的算法也是图计算(图论)中的常用算法,包括BFS、DFS、PageRank等等,大家如果嫌麻烦,可以找个图数据库,比如neo4j,这些算法已经集成在它的基本查询能力中。
接下来介绍天弘在数据资产方面的一些治理实践。
由于业务迭代等历史原因的影响,天弘共形成了三套数据应用及技术体系。
前面介绍了,围绕余额宝这支基金已经形成了一个完善的技术生态体系,这套体系每天会产生大量的数据,包括和支付宝的交易对账数据、系统的增量数据等等。针对余额宝的海量数据的存储、分析、消费,我们专门构建了一套云上的数仓来支撑,我们称之为电商大数据中心,主要采用蚂蚁金融云上的ODPS+DataWorks这套技术体系来构建。它的整个模型的设计完全按ODPS的方法论来设计,ODS数据会按DWD(明细数据)、DWS(汇总数据)两层结构来进行汇总。汇总后的数据会根据不同的用途分门别类进行存储,比如,用于离线分析的,会被存储到ODPS中;用于实时分析的,会被存储到ADS中;主数据或者供业务使用的数据,则会被存储到RDS中。数据整理好之后,我们就可以在上面进行余额宝销售情况的分析、流动性分析、或者在其上做一些特征工程分析和数据打标工作,以便做一些精细化的用户运营,这是我们的电商大数据技术体系。
天弘除了余额宝这支产品外,还有其它几十上百支的基金产品,针对所有基金产品的销售数据的分析也必须有相应的数据支撑手段,这部分工作主要由我们数据中心在负责,数据中心构建了一套Hbase技术体系的数仓来支撑所有基金销售系统中的账户、资产、交易数据的抽取、存储和分析,这套数仓采用的是典型的维度建模法,所有的基础数据最终会被汇总成各个主题下的宽表、维表及事实表。这套数仓主要用来生成针对销售的财务分析报告、监管报送报告等等。公司各业务线都在使用的报表中心也是在这套数仓基础上构建的。
以上两套数仓主要面向的是基金销售。基金公司最核心的两大业务,一个是基金销售,另外一个是投资,投资是专业性很强的技术活,哪支股票或债券值得买,什么时候买入,什么时候卖出,这些都需要有数据的支撑,所以投资研究团队也建了一个云下的Oracle数仓,会将企业财报、行情数据、舆情信息等相关数据灌入这套数仓中。由于指标维度特别多,这套数仓采用了cube建模,利用雪花模型最终构建了一个非常多层的数据集市,再基于这个数据集市进行各个投资领域的分析,包括股票及债券的信用评级,持仓分析、企业经营健康度的跟踪预警等等。这套数仓总的数据量并不是特别大,它更偏向传统的BI分析,但指标维度要远超前两套数仓。
以上是我们目前主要使用的三套数仓,面向不同的业务领域。但多套数仓的存在也相应带来了一些问题,首先是重叠度高,维护成本也较高,毕竟技术体系都不一样,学习门槛较高,相关数仓使用团队很难进行交叉支援;多套数仓的存在,客观上也限制了数据在公司内部的流动效率,一个数仓的使用者要使用另外一个数仓的数据,需要对应数仓维护人员的支持,数据使用成本较高;多套数仓也容易导致数据口径不一致,数据标准不统一;数据质量和安全方面的管控也很麻烦。
为了解决以上问题,我们在17年底立了一个数据治理的项目,希望通过建立统一的实时数仓,来统一公司内部的多个数仓的数据指标、数据模型和数据,以形成公司的统一数据视图,并构建相应的数据门户、数据服务接口,结合数据质量及安全管控,最终实现数据在公司内部更好的流动和分享,发挥数据应有的价值。通过数据治理工作,我们希望降低数据的总使用成本,提高数据开发的效率,并构建公司一级的大数据技术体系。
数据治理是一项复杂的体系性工程,其中包含了数据存储,ETL、任务调度、数据质量及安全管控、数据门户及数据接口,以及在此基础上的一整套方法论。
天弘本质上是一家金融公司,由我们自己独立去构建这么一套数据治理体系明显不现实,好在市面上有不少相关产品,因此,一开始我们定的策略是“借力“,希望借助外部力量来完成这个工作。我们评估了一些有代表性的数据治理产品及数据中台产品,综合分析后发现,单纯落地一套数据治理或数据中台的技术体系并不难,难就难在对现有数据指标、模型的迁移工作上。指标和模型是和业务息息相关的,对其进行迁移首先必须全方位的了解业务,试问又有谁能比我们自己更了解业务?这个工作我们自己都做不来的话,指望别人就更不现实了。
综合评估之后,我们最终决定还是自己动手,尤其是要将模型的设计牢牢掌控在自己手中,自力更生为主,同时辅助使用一部分外力。
由于数据治理工作体系复杂,需要长期迭代和优化,而且前期需要构建包括统一数仓在内的基础设施,因此不能完全的业务需求驱动,必须将基础设施的构建工作也提升到和业务需求对等的水平,才能保证底层能力不缺失。对于一些短期内很难构建完善的能力,比如数据血缘的梳理能力,我们会使用一些临时方案来替代,再在未来能力成熟时进行优化或替换。
数据治理的最终目标是要保证数据的一致性、准确性,这需要一整套的流程规范来保障。流程规范如果只是存在于纸面上,是很难落地的,必须通过技术手段来强制进行贯彻。同时,还需要构建一套知识库,来维护和沉淀所有指标、模型的定义。避免数据干系人对同一个指标或者模型理解出现歧义和偏差,因为一旦出现理解偏差,必然会导致数据的不一致性。
以上就是我们在数据治理上的选型策略及实施策略。
来看一下数据治理工作最终落地的整体技术架构。在整个技术架构中,重点开发了3个能力,第一个是元数据管理,用来管控模型;第二个是解析代理层,用来隔离模型和物理数据;第三个是流程/工单模块,用于保障数据管理及开发规范的落地。
我们希望通过模型,将顶层使用数据的用户(或系统)和底层的物理数据进行严格的隔离,并为此开发了完善的元数据管理能力,其中包含了指标管理、维度管理、模型管理、数据表管理这四大管理模块。同时通过数据门户中的数据地图,创建相应的数据域,数据域下挂主题,主题下再挂指标、模型、数据表,通过这样的一种树状结构,可以让数据使用者快速的检索和定位他所需要的模型。由于用户主要接触的是模型,因此必须保证模型的完备及准确,我们推荐使用者通过平台提供的元数据管理能力来构建和使用模型及数据,这样就能很好的管控模型。如果由于一些特殊原因,导致不得不使用SQL或者Python这类的脚本来构建物理表的话,我们也会通过一些辅助工具进行脚本的反向解析,以识别出模型;如果识别不了,就通过工单流程对脚本进行严格审核,并在流程环节中由特定数据开发人员将模型补全,以保证模型不发散。
虽然用户接触的都是模型,但在执行数据查询或变更操作时,还是要转换成可执行的SQL、HQL等脚本。这时,就需要依托解析代理层来做这个工作,通过它的模型解析器,将模型转换成可执行脚本;再通过一个异步任务调度服务集群来进行长时间的数据查询或变更操作。
由于通过模型隔离了底层的物理存储,所以在存储的选择上就能很灵活,可以根据数据的热度及数据量的大小来选择不同的存储服务,存储这层主要通过数据适配器来负责。
模型的严格管控带来了一系列的好处。首先,比较容易进行安全控制,能够进行从数据域、数据主题、一直到数据表、字段一级的授权控制。基于模型也很容易识别出数据血缘,可以清楚的知道数据来自于哪,存储在了哪里,又被哪些第三方系统所使用。完整的数据血缘有助于控制指标的联动影响,因为指标之间往往由于数据的关系产生关联,一个指标变动之后,可能会影响到其它指标,可以通过数据血缘来识别指标影响的范围,以实现更精细的ETL调度策略。
通过治理工作构建了较完善的质量管理机制,通过批处理引擎可以定期运行一系列预定义的检查规则,进行“脏数据”的检测,并根据检测结果自动生成质量报告。
为了严格落地数据开发规范及数据使用规范,还在数据门户中引入了流程引擎,基本上实现“一切皆工单,一切皆流程”。所有的数据使用申请,开发申请都需要在数据门户中发起工单,并走相应的审批流程,可以说,流程是落地规范的最好工具。
对于第三方系统的数据应用需求,我们同步提供了数据服务接口,提供“推”和“拉”这两种数据共享方式。
最后,说一下如何让企业的数据真正成为数据资产。一方面,我们要持续的进行数据治理工作,以保证数据的一致性、准确性、实时性,让数据有较高的质量,并降低数据的使用难度;另外一方面,也需要不断提高人员对数据的重视程度。
因为数据看的到但摸不着,所以很多人容易在本能上轻视它,一种有效的做法是对数据相关的工作进行收益度量。比如说,数据开发团队帮助业务部门做了一个数据打标的工作,可以要求业务部门必须对这个数据开发工作的收益进行评估,比如帮助业务部门提升了10%的新客转化率。这样,通过定量的价值度量可以让大家直观的体会到数据及数据开发工作带来的收益,长久以往,就能不断的提高对数据的重视程度,真正做到将数据视作一种有价值的资产。
最后,介绍一下我们在团队协同方面的一些治理实践,这部分是前面服务治理内容的延续,主要针对的是线下环节的治理。
服务化之后,每个团队负责一部分的服务,经常一个业务会涉及多个团队之间的协同配合,如何让团队之间的协作更高效,我们也做了不同的尝试,从综合效果来说,敏捷模式会更适合一些。以我们移动平台团队举例,目前采用两周一迭代、固定发版的模式,同时每个迭代之内,采用“火车发布模式”,实行班车制,准点发车。这样,其它协作部门在很早之前就能大概知道我们的发布计划,产品方面也大概知道要把需求放入哪个迭代中。这种模式能够有效降低部门间的沟通成本。在每期工作量评估时,一般会预留一些工作量buffer,以应对临时性的需求,这类需求不受版本约束,按需发布。如果这个迭代周期内没有紧急需求,我们会从backlog中捞一些架构优化的需求来填补这些buffer。
为什么会持续有一些架构优化的活呢?因为经常有一些紧急需求,使用正常开发方案可能无法如期上线,但业务又要的比较急,必须用一些应急性的临时方案先上线使用。这种时候,我们会同步创建一个架构优化的任务放到backlog中,等后续有时间再安排若干个迭代周期将这个临时方案进行重构优化,以防止临时方案变成长期方案,导致整个架构被腐化。因此我们的backlog池中,总是存在这类的架构优化任务。
对每个迭代而言,最不可控的就是UI的设计了。UI设计感性因素更多,可能会反复修改,不像程序代码那么明确。所以,我们一般不将UI设计纳入迭代中,而是将其作为需求的一部分,在每个迭代开始之前的工作量评估中,要求必须提供完整的UI物料,否则不予评估工作量,此需求也不会被纳入迭代之中。
对团队的协同效率同样需要进行度量和治理。我们采用数据驱动的精益看板方法,持续采集每个迭代中各个阶段的过程指标事件,比如任务什么时候完成设计、什么时候进入开发、什么时候开发结束、什么时候进入测试...等等。这些过程指标被汇总后,会在其基础上制作出精益看板中的几大典型报表:
韦伯分布图
累积流图
价值流图
控制图
通过这几个报表,结合精益看板就可以对需求流动的效率进行持续的评估,看研发管道是否有堆积、是否有阻塞、并进行实时的干预和治理。注意,这里评估的是协同的效率,而不是研发资源的利用率,对研发资源的利用率,我们有另外的度量指标和报表。
举个例子,比如说,在看板上看到“开发完成”这个步骤堆积了一些任务,由于敏捷协作是基于“拉动”式的,说明“测试”这个环节的处理能力出现了问题,才导致前置的“开发完成”环节形成堆积。这时候就需要去了解测试究竟出现什么样的问题、问题原因是什么,并解决问题,以保证整个研发管道的流动是顺畅的。
每个迭代持续的进行精益看板的数据分析和度量,并通过治理策略进行不断的改进优化,可以让我们的研发协同越来越顺畅。
针对代码质量的管理,常规的做法除了代码的codereview外,一般还会使用Checkstyle,FindBugs,Jtest这类静态代码扫描工具来做代码的缺陷扫描。但这类工具只能扫描单个类或文件的缺陷,对跨类的多层循环嵌套这类的问题就无能为力了。为此,我们开发了针对这类缺陷的静态代码扫描工具,这个工具和前面介绍的扫描代码的静态调用链路的工具实际上是同一套。除了深层的代码缺陷外,利用这个工具还可以对代码的注释完备性及注释密度进行检测,毕竟人员的流动是客观存在的,开发人员迟早要将自己的开发代码移交出去,完备的注释可以降低新人接手的难度。
除了代码质量之外,还可以结合线上bug的种类和数量,来综合评估开发人员的开发质量。因为,代码是人写的,bug也是人制造出来的,通过结合开发人员开发的代码的质量,及他的产出物产生的异常等级(类型及数量),对开发人员的开发质量进行综合度量。我们这里采用相对指标,而不是绝对指标,防止陷入“做得越多,错得越多”的怪圈。当然,实际中还会结合其它的指标,但这两个是最主要的指标。通过这两个核心指标,可以生成研发人员开发质量综合评估报告。
进一步汇总个人的质量综合评估报告,可以获得针对团队的开发质量综合评估报告。这两个报告本质上就是个人及团队的研发质量“画像”,完全可以作为个人及团队KPI考核的重要参考。在此基础上,还可以通过这两个报告的变化趋势(时间纵比),来促使开发人员和开发团队不断进行开发质量的改进和开发技能的提升。
服务化架构下的测试治理的两大核心诉求:一是提高测试的覆盖度,具体说就是提高需求覆盖度、代码覆盖度、页面覆盖度;二是降低测试用例的维护成本。
先讨论测试覆盖度。
需求覆盖度,可以通过服务这个维度来对需求及测试用例进行关联,找出每个需求所对应的单元测试用例、自动化测试用例、手工测试用例,还可以把多个开发迭代周期的这些指标进行时间维度的纵比,以得出需求覆盖度的变化趋势。
代码覆盖度,有很多的工具帮我们来做,比如contest或者JaCoCo这类的工具,这里不再赘述。
页面覆盖度,可以将每次集成测试中调用的页面以日志的形式记录下来,再通过日志的聚合分析,结合工程源码的扫描,两厢一比较,就可以统计出哪些页面是没有被覆盖到的。
测试用例的维护成本分两块,一块是新增用例的维护成本,这个比较好度量;比较麻烦的是存量测试用例的变更度度量,我们采用相似度匹配算法,先算出存量测试用例前后两个版本代码的相似度,再换算成变更度。
通过对测试的这两大类指标的不断度量和治理,可以实现测试工作的整体“降本增效”。
在服务化的过程中,研发最大的痛点一定是调试。原来单体应用中的服务被拆分到不同团队,并部署在不同的服务器上,而本地只有一个服务接口。这时候要做调试,要么做P2P直连,这需要搭建不同开发版本的集群,成本较高;要么做MOCK,采用传统的MOCk手段,要写一堆的MOCK语句,比如用mockito,你要写一堆的when…..thenReturn….的语句,耦合度非常的高。
我们利用分布式服务框架提供的过滤器机制,开发了一个Mock过滤器,通过Mock数据文件来详细定义要被mock的服务的名称、入参及出参。这样,当请求过来时,将服务名及入参和mock数据中的定义进行比对,结果吻合,就直接将mock数据文件中的出参反序列化后作为服务的调用结果直接返回,同时远程调用的所有后续操作被终止。这样,通过mock数据模拟了一个真实的远程服务。通过这种方式来构建服务的mock能力,我们就不需要写一堆的mock代码了,而且整个过程对业务逻辑来说毫无感知,完全把mock能力下沉到底层的服务框架。
另外,为了有效降低制作mock文件的成本,我们开发了一系列辅助工具,可以基于服务接口直接生成mock文件的框架。我们还基于服务框架的过滤器机制开发了“在线数据抓取过滤器”,它可以将指定的服务请求的入参和返回结果都抓取下来,直接写成mock数据文件。通过抓取方式获得的mock数据文件,往往有更好的数据质量,毕竟反映的是更加真实的业务场景。不过,这里还有一个合规性的问题,一定要做好数据脱敏的处理工作。对于我们,目前只在测试环境中进行数据抓取操作。
我们的综合调测能力是综合P2P直连和Mock两种方式来共同构建的。在项目迭代的早期,前端团队和服务端团队,中台开发团队和后台开发团队会先定义接口,再基于接口直接生成mock文件,这样大家就可以并行开发。开始时,服务都没有开发出来,mock比例是最高的;随着迭代的进行,服务被不断开发出来,并部署到线上,这时,P2P直连调测的比例会上升,Mock的比例会下降;一直到集成测试时,只剩下P2P直连的模式,没有mock了。
通过以上的调测能力构建,可以有效改善服务化架构下团队的开发效率,而且团队规模越大,这种调测体系的效果越明显。
以上就是我这次分享的全部内容。
活动推荐
2020年4月17日,北京,Gdevops全球敏捷运维峰会将开启年度首站!重点围绕数据库、智慧运维、Fintech金融科技领域,携手阿里、腾讯、蚂蚁金服、中国银行、平安银行、中邮消费金融、中国农业银行、中国民生银行、中国联通大数据、浙江移动、新炬网络等技术代表,展望云时代下数据库发展趋势、破解运维转型困局。