微服務(wù)架构和领域驱动设计应用(yòng)实践

2019/08/27      3089 文(wén)章来源:亿级流量网站架构

微服務(wù)架构几乎都是从 ALL IN ONE 的单體(tǐ)架构演进而来,中间又(yòu)经历了分(fēn)布式架构、面向服務(wù)架构的演进过程。

单體(tǐ)架构往往以烟筒式方式发展,往往存在两个主要问题:中心化和耦合度高。所谓中心化,就是数据集中存储在单个数据库中,业務(wù)系统集中部署在单台服務(wù)器上,通过集群部署方式提供服務(wù)能(néng)力,然而中心化的问题,也就是单点问题。而耦合度高,主要是指其中一个功能(néng)模块升级,其它的模块都得一起升级。这里要说明下,模块依赖度高不是单體(tǐ)架构的错,是因為(wèi)本来架构可(kě)能(néng)就没有(yǒu)设计好,但是,在实际场景中,随着快速迭代开发,研发换了一波又(yòu)一波,产品走了一茬又(yòu)一茬,难免系统架构腐化严重。

看到了单體(tǐ)架构的诸多(duō)问题,系统开始通过按功能(néng)或模块进行拆分(fēn),拆分(fēn)成多(duō)个独立的子系统,系统间通过 RPCMQ 方式调用(yòng),由此逐渐演变為(wèi)分(fēn)布式的服務(wù)架构。分(fēn)布式服務(wù)架构主要通过服務(wù)化和层次化进行解耦拆分(fēn),《架构整洁之道》书中提到一点,系统可(kě)以降解為(wèi)策略和层次,而架构设计就是把相同的策略分(fēn)到同一个组件中,反之,分(fēn)属于不同的组件。所以,架构设计可(kě)以通过分(fēn)层设计,由高层次服務(wù)调用(yòng)低层次服務(wù),低层次服務(wù)通过接口向上提供服務(wù),以实现系统之间的解耦。也正是在这一阶段,单體(tǐ)架构一下子被拆分(fēn)成几个或几十个系统。

但是随着架构的发展,问题又(yòu)接踵而来,在没有(yǒu)做好边界的划分(fēn)之前,系统拆分(fēn)的服務(wù)往往是松散的。正如《架构整洁之道》所讲:软件架构设计本身就是一门划分(fēn)边界的艺术。所以,很(hěn)多(duō)系统在这一阶段,又(yòu)往往进行了服務(wù)内聚和去层次化的演变过程。所谓服務(wù)内聚,就是以领域驱动设计為(wèi)原则,重新(xīn)界定领域边界,对模块进行服務(wù)整合,对系统进行合并。而去层次化,则是去除服務(wù)层次高低之分(fēn),按服務(wù)调用(yòng)最优链路提供服務(wù)请求,降低深度,以此保证系统的稳定性能(néng)。

系统如何从 0 1,从 1 2,从 2 100,在具體(tǐ)实践微服務(wù)的过程并不容易。首先,考虑的第一个问题是:微服務(wù)是什么?其实,微服務(wù)是一个动作:拆。说到拆,就涉及到两个问题,第一,怎么拆?第二,拆到什么程度。

第一个问题,关于怎么拆,需要关注两个层面,一是发版速度,如商(shāng)品、交易、促销等不同领域的迭代速度和开发速度是不一致的,所以,应该拆分(fēn)到不同的领域,否则,就可(kě)能(néng)耦合在一起,A 上線(xiàn) B 必须跟着一起上,这可(kě)能(néng)就应该合并到一起。另一个是能(néng)源协同,每个系统后面对应不同的研发团队,一个项目往往需要几个团队分(fēn)工协作,那么系统拆分(fēn)必然导致团队协作的成本,所以拆分(fēn)系统同样也是拆分(fēn)团队,要考虑协作沟通所带来的成本。

第二个问题,拆到什么程度。其实,已经有(yǒu)很(hěn)多(duō)人对微服務(wù)进行了定义,其中我认為(wèi)最重要的两点:一是微服務(wù)或一组微服務(wù),应该是一套独立部署运行的服務(wù),二是微服務(wù)所依赖的数据资源应该是彼此间相互独立隔离部署的。

其实,拆只是实践微服務(wù)的开始,要搭建整體(tǐ)的微服務(wù)框架还要进行四部曲:拆、服務(wù)化、高可(kě)用(yòng)、隔离。

1、服務(wù)拆分(fēn)

微服務(wù)讲究拆分(fēn),那么多(duō)微才是微,以及,怎么才是比较合理(lǐ)的拆分(fēn)。在架构设计领域,有(yǒu)一个大家都在讲的著名定律:康為(wèi)定律,可(kě)以理(lǐ)解為(wèi):一个系统架构的组织关系是和团队的组织关系相匹配的。如交易系统会对应一个交易系统的研发团队,商(shāng)品系统会对应一个商(shāng)品系统的研发团队,这种职责明确的组织结构会使得系统开发更高效。

《未来架构》一书中讲过一个 AKF 立方體(tǐ)模型,从三个纬度讲述功能(néng)拆分(fēn)、水平扩展、数据分(fēn)區(qū)所产生的复杂度,如果只有(yǒu)一个系统,单台部署,单点存储,那么它就应该只是一个点,因為(wèi)随着量级的增大,系统在每个纬度不断延長(cháng),逐渐成為(wèi)一个庞大的立方體(tǐ)。

拆分(fēn)可(kě)以分(fēn)為(wèi)系统拆分(fēn)、功能(néng)拆分(fēn)和读写拆分(fēn)。如一个单體(tǐ)系统中,按照用(yòng)户的交互场景看,门户首页可(kě)以拆分(fēn)為(wèi)前台系统,类目、商(shāng)品、搜索等功能(néng)可(kě)以拆分(fēn)到商(shāng)品中心,订单、结算、发票等功能(néng)则可(kě)以拆分(fēn)到交易中心,这就是简单的系统拆分(fēn)和功能(néng)拆分(fēn)。而有(yǒu)些功能(néng)较為(wèi)特殊,如商(shāng)详页,在读取时需要聚合读取,所以又(yòu)可(kě)以进行读写拆分(fēn)。

2、服務(wù)化

微服務(wù)首先需要有(yǒu)微服務(wù)基础设施,没有(yǒu)微服務(wù)基础设施,实践微服務(wù)就是一场灾难。以 SOA 落地方式為(wèi)例,SOA 落地方式主要有(yǒu):分(fēn)布式服務(wù)化和集中式管理(lǐ)(ESB),分(fēn)布式服務(wù)化的技术手段有(yǒu) dubbo spring cloud 等等,必须有(yǒu)整套的如服務(wù)发现、服務(wù)订阅、服務(wù)监控、服務(wù)追踪、服務(wù)日志(zhì)等微服務(wù)基础设置,才能(néng)进行微服務(wù)架构。不能(néng)简单只有(yǒu) p2p 的服務(wù)调用(yòng)就开始服務(wù)化,那是不现实的。

3、服務(wù)治理(lǐ)

当服務(wù)拆分(fēn)的设计方案确认完毕,而服務(wù)化的基础设施也部署到位,那么系统往往一下子就会突然发布出成百上千个服務(wù)接口,就像一个网络一样,每个服務(wù)或微服務(wù)就像网中的一个节点,彼此之间关联和联系。但是,服務(wù)网络应该被设计成為(wèi)一个有(yǒu)向无环图,否则,就是一团麻。如开始的时候,只是 A 调用(yòng) B,但随着业務(wù)发展,需要修改,但是因為(wèi)对 A 不了解,就加个环节 B 调用(yòng) A,如此形成了环形调用(yòng),不仅逻辑复杂了,还降低了稳定和性能(néng)。

这里的服務(wù)治理(lǐ)不是讲中间件团队的服務(wù)治理(lǐ),如超时优化、启动优化等等,而是作為(wèi)应用(yòng)平台对服務(wù)的治理(lǐ)。服務(wù)治理(lǐ)又(yòu)分(fēn)為(wèi)三个阶段:服務(wù)梳理(lǐ)、服務(wù)界定和服務(wù)编排。所谓服務(wù)梳理(lǐ)就是梳理(lǐ)系统对外开放的服務(wù)化接口,包括服務(wù)的 provider consumer,以及服務(wù)分(fēn)组、动态路由等依赖的梳理(lǐ),然后对拆散的服務(wù)进行归类、界定,确定服務(wù)领域从属性,依据领域模型重新(xīn)界定服務(wù)边界,最后通过服務(wù)迁移、切换,对同一领域的服務(wù)接口进行服務(wù)整合,提供统一的服務(wù)出口,实现服務(wù)编排。

為(wèi)什么要进行服務(wù)治理(lǐ)?那先来看看不进行服務(wù)治理(lǐ)的坏处。

微服務(wù)化拆分(fēn)必然会在初期产生代码到处拷贝,没有(yǒu)一套代码,必然会造成复杂的扩散,如同样语意的 AB 两个接口,如果 A 接口存在性能(néng)问题需要加缓存,那么 B 接口也会存在同样的问题,并需要同样的改造,这样复杂度就会到处蔓延,同时也无法实现统一的服務(wù)层架构,还有(yǒu),如果同样语意或相似语意的接口太多(duō),那么接口就是混乱的,无法实现有(yǒu)限接口的治理(lǐ),如果系统出了问题,可(kě)能(néng)很(hěn)难定位问题。业務(wù)逻辑是依赖于数据的,如果接口是混乱的,那么慢 SQL 等质量差的 SQL 就无法进行有(yǒu)效收口改造,同样就更加难以实现数据库拆分(fēn)和解耦。综上,服務(wù)治理(lǐ)的过程就是从无序到有(yǒu)序的过程。

总结一下看从拆分(fēn)到服務(wù)化的过程,就是将原来一个整體(tǐ)的服務(wù)打碎,碎成一块一块的零碎服務(wù),然后再对服務(wù)重新(xīn)归类、整合,形成一个一个新(xīn)领域的、独立的服務(wù)。如单體(tǐ)架构就像一个宏伟的城堡,如果相对其中一个点进行调整和改造,那么可(kě)能(néng)面临着牵一发而动全身的风险。

微服務(wù)化就是以一系列小(xiǎo)的服務(wù)去支撑一个应用(yòng)的方法论。简明扼要的说,就是:分(fēn)而治之。

4、服務(wù)高可(kě)用(yòng)

服務(wù)拆分(fēn)并服務(wù)化之后,是不是就完事了,不!真正的微服務(wù)实践才刚刚开始,简单提供了一个接口,是没有(yǒu)意义的,它必须具备高可(kě)用(yòng)、高性能(néng)、高并发的三高特性,尤以高可(kě)用(yòng)最為(wèi)重要。保障服務(wù)高可(kě)用(yòng)的方法有(yǒu)很(hěn)多(duō),如数据异构、多(duō)级缓存、超时与重试、熔断、异步并发、降级、限流、消息队列、压测与预案等。

重点说下数据异构,所谓数据异构,就是将通过顺序消费或并发消费的方式,订阅 MySQL 数据库的 binlog,然后通过消息,如 Kafka MQ 方式,异地存储到缓存或其它数据源中,如 RedisElasticsearch 等。数据异构现在被普通使用(yòng),实现数据聚合,形成数据闭环。

在进行数据异构的过程中,需要关注几个关键点,一是 CAP 原则,即强一致性往往被舍弃,而采用(yòng)最终一致性的设计。二是缓存,缓存在微服務(wù)架构中,不能(néng)被当成银弹来使用(yòng),使用(yòng)缓存必须正视的问题有(yǒu):热点缓存 & Value 缓存、缓存穿透等问题。三是消息,消息现在还存在的问题有(yǒu):延迟问题、消息的不稳定性(如丢消息)、消息的不确定性(如 timeout)、消息补偿、柔性事務(wù)等问题。而这些是微服務(wù)高可(kě)用(yòng)架构实践中,不能(néng)不面对的问题。

5、服務(wù)隔离

隔离也是服務(wù)拆分(fēn)的一种,為(wèi)什么单独讲?因為(wèi)服務(wù)拆分(fēn)、服務(wù)化、服務(wù)治理(lǐ)和服務(wù)高可(kě)用(yòng)都是在事前或事中可(kě)以做的,然而,有(yǒu)些系统已经成了某个样子,那么我们接收时,首先能(néng)做到的就是迅速对系统进行隔离,保证业務(wù)之间不互相影响,具體(tǐ)实践包括:进程線(xiàn)程隔离、集群/机房隔离、读写隔离、动静隔离、爬虫/热点隔离等。

从单體(tǐ)架构开始,到分(fēn)布式架构设计、微服務(wù)架构,再到现在领域驱动设计,作為(wèi)实践微服務(wù)的架构师,思想上又(yòu)有(yǒu)什么变化?

我觉得,程序员都是很(hěn)闷骚的,但每个程序员心里都住一个大侠,金庸《笑傲江湖(hú)》里有(yǒu)气宗和剑宗之分(fēn),什么是剑宗,我理(lǐ)解就像是剑招、招数,如 springspring bootspring cloud,还有(yǒu) tomcatnettyserviceless 等,这些技术、中间件、框架等就像脚手架一样可(kě)以帮助我们提高效率,但是,我们是否需要出现一个新(xīn)技术就學(xué)习一个技术呢(ne)?而这就让我想到了气宗,它就像是通过对内在规律的掌握,打通任督二脉,实现融会贯通,正如《九阳神功》所说:他(tā)强任他(tā)强,清风拂山(shān)岗,我自一口真气足。如 KakfaFlinkHbase 等分(fēn)區(qū)可(kě)用(yòng)性保障的设计思想都是基于 BigTable 的分(fēn)區(qū)复制策略。

正是重心由外到内的转变,让我认知到,现在系统架构的发展,很(hěn)多(duō)系统架构的复杂度,已经不是简简单单通过引入一些新(xīn)技术或框架,就能(néng)降低系统复杂的。它需要大大的提前对风险、问题、隐患的预知,做到未雨绸缪。《从零开始學(xué)架构》书中提到:架构设计的发展历程就是在于降低软件系统复杂的历程。

《架构整洁之道》书中提到:一个软件系统存在的意义,是系统用(yòng)来赚钱或省钱的那部分(fēn)代码,那才是整个系统的皇冠明珠。所以,当拿(ná)到一个需求的时候,首先考虑的是要解决什么问题,它的问题域是什么,而不是先考虑用(yòng)哪种技术去实现或解决这个问题,这是本末倒置的处理(lǐ)方法。

从领域驱动设计的角度来看,需求首先是问题域,属于业務(wù)边界的事情,梳理(lǐ)了解要实现什么功能(néng)和需求,然后在外延到工作边界,确认团队合作的方式,因為(wèi)很(hěn)多(duō)需求都可(kě)能(néng)需要跨团队合作的,最后才会到应用(yòng)边界,即技术实现的解决问题领域。

上图是一个典型的洋葱头模型,它的思想就是阐述了从内到外的思考过程。然后,现实工作中,经常有(yǒu)人从外往内去考虑问题,从我过往的经历举例,一次是数据异构的方案,之前是采用(yòng) Storm,后来 flink 开始流行,我就将业務(wù)切换到了 flink,但是对于 flink 的调优不到位,从而影响了系统稳定性,还有(yǒu)一次是之前搜索用(yòng) solr,后来 es 开始流行,就像切到 es,但是 es solr 两套搜索引擎的搜索排序结果是不一样的,最后,我们只能(néng)强奸了业務(wù)。其实,这都是不太对的。《架构整洁之道》也提到:良好的架构设计应该尽可(kě)能(néng)地允许用(yòng)户推迟和延后决定采用(yòng)什么框架、数据库、Web 服務(wù)以及其他(tā)与环境相关的工具。

理(lǐ)解领域驱动设计,需求或问题它开始于一个领域意愿,由领域专家和交付团队一起,输出统一语言和领域知识,统一语言可(kě)以是敏捷看板,因為(wèi)一个团队是由产品、运营、研发、测试组成的,彼此之间是基于统一语言进行沟通的,然后定义领域边界,區(qū)分(fēn)核心领域、普通领域、支持领域,其中核心领域就是由核心团队开发的,支持领域可(kě)以是外包等实现的,在通过映射上下文(wén),确认限界上下文(wén),最终确认系统应用(yòng)边界。

采用(yòng)领域驱动设计的六边形架构绘制了一张交易系统的领域边界图,不同颜色表明不同领域,U/D 代表上游和下游。这个图还不算完整,因為(wèi)领域业務(wù)逻辑是要為(wèi)端服務(wù)的,而通过对领域的梳理(lǐ),可(kě)以通用(yòng)的支持很(hěn)多(duō)端。即表达為(wèi)领域有(yǒu)界,端无界。最终,每个限界上下文(wén)即是微服務(wù)。


其实,每个系统的微服務(wù)架构演进道路各不相同,所以,实践微服務(wù)不能(néng)一板一眼原封照抄,而是要根据自己的业務(wù)特征,合理(lǐ)的裁剪,找到合适落地方案。总结一句话,那就是:因需而变。虽然微服務(wù)的路径是不一样的,但方向是相同的。再次回顾微服務(wù)的架构演进历程,你会发现,从单體(tǐ)架构开始,进行拆分(fēn),形成微服務(wù)之后,又(yòu)因為(wèi)各种这样的原因,很(hěn)多(duō)微服務(wù)架构又(yòu)在进行合并,现在领域驱动设计来了,又(yòu)开始重新(xīn)拆分(fēn)。真可(kě)谓:话说天下大势,分(fēn)久必合,合久必分(fēn)。