赞
踩
思考:
带着问题,看下面分析:
“设计原则千万条,高内聚低耦合第一条,架构设计不规范,开发运维两行泪!”
在分布式架构下,单体应用被拆分为多个微服务,为了保证微服务的单一职责和合理拆分,“高内聚、松耦合”是最宝贵的设计原则。
通俗点讲,高内聚就是把相关的行为聚集在一起,把不相关的行为放在别处,如果你要修改某个服务的行为,最好只在一处修改。如果做到了服务之间的松耦合,那么修改一个服务就不需要修改另一服务,一个松耦合的服务应该尽可能少的知道与之协作的那些服务的信息。
从集中式架构向分布式架构的技术转型,正如从盖砖瓦房向盖高楼大厦转变一样,必然要有组织、文化、理念和设计方法的同步更新,其中最不可或缺的能力就是架构设计能力。
如何做到“高内聚、低耦合”?我们先来学习几种典型的微服务架构模型。
在整洁架构里,同心圆代表应用软件的不同部分,从里到外依次是领域模型、领域服务、应用服务、最外围是容易变化的内容,如界面和基础设施(如数据存储等)。整洁架构是以领域模型为中心,不是以数据为中心。
1.1 整洁架构
整洁架构最主要原则是依赖原则,它定义了各层的依赖关系,越往里,依赖越低,代码级别越高。外圆代码依赖只能指向内圆,内圆不知道外圆的任何事情。一般来说,外圆的声明(包括方法、类、变量)不能被内圆引用。同样的,外圆使用的数据格式也不能被内圆使用。
1.2 整洁架构各层主要职能如下:
追溯微服务架构的渊源,一般会涉及到六边形架构。六边形架构的核心理念是:应用是通过端口与外部进行交互的,这也是微服务架构下 API 网关盛行的主要原因。六边形架构中,内部业务逻辑(应用层和领域模型)与外部资源(APP,WEB 应用以及数据库资源等)完全隔离,仅通过适配器进行交互。它解决了业务逻辑与用户界面的代码交错的主要问题,从而可以很好的实现前后端分离。
2.1 六边形架构
六边形架构将系统分为内部和外部两层六边形,内部六边形代表了应用的核心业务逻辑,外部六边形代表外部应用、驱动和基础资源等。内部通过端口和适配器与外部通信,对应用以 API 主动适配的方式提供服务,对资源通过依赖反转被动适配资源的形式呈现。一个端口可能对应多个外部系统,不同的外部系统使用不同的适配器,适配器负责对协议进行转换。这就使得应用程序能够以一致的方式被用户、程序、自动化测试、批处理脚本所驱动。
六边形架构各层的依赖关系与整洁架构类似。
2.2 CQRS(命令与查询职责分离)
CQRS 就是读写分离,读写分离的主要目的是为了提高查询性能,同时达到读、写解耦。而 DDD 和 CQRS 结合,可以分别对读和写建模。
CQRS(命令与查询职责分离)
对于写少读多的共享类通用数据服务(如主数据类应用)可以采用读写分离架构模式。单数据中心写入数据,通过发布订阅模式将数据副本分发到多数据中心。通过查询模型微服务,实现多数据中心数据共享和查询。
3.1 分层架构的好处
由于层间松散的耦合关系,使得我们可以专注于本层的设计,而不必关心其他层的设计,也不必担心自己的设计会影响其它层,对提高软件质量大有裨益。其次,分层架构使得程序结构清晰,升级和维护都变得十分容易,更改某层的代码,只要本层的接口保持稳定,其他层可以不必修改。即使本层的接口发生变化,也只影响相邻的上层,修改工作量小且错误可以控制,不会带来意外的风险。
关于分层架构的权威观点,Martin Fowler 在《Patterns of Enterprise Application Architecture》一书中给出了答案
要保持程序分层架构的优点,就必须坚持层间的松耦合关系。设计程序时,应先划分出可能的层次,以及此层次提供的接口和需要的接口。设计某层时,应尽量保持层间的隔离,仅使用下层提供的接口。
3.2 DDD(领域驱动设计)分层架构
DDD 分层架构各层定义与职能:
虽然整洁架构、六边形架构以及 DDD 分层架构三种架构模型展现方式以及解决问题的出发点不一样,但其架构思想与微服务架构高内聚低耦合的设计原则高度一致。
突破现象看本质,在变与不变中寻找平衡!
上述三种架构模型正是通过分层方式来控制需求变化对系统的影响,确保从外向里受需求影响逐步减小。面向用户的展现层可以快速响应外部需求进行调整和发布,灵活多变,应用层通过服务组合和编排实现业务流程的快速适配上线,领域层基本就不需要太多的变化了。这样设计的好处是可以保证领域层的核心业务逻辑不会因为外部需求和流程的变动而调整,对于建立前台灵活、中台稳固的架构能力是很有好处的。
中台和微服务设计的关键在于合理的分层和领域模型的设计!
中台属于后端业务领域逻辑范畴,重点关注领域内业务逻辑的实现,通过实现公共需求为前台应用提供共享服务能力。按 DDD 的方法,在领域模型建立的过程中会对业务和应用进行清晰的逻辑和物理边界划分。领域模型的设计结果会影响到后续的系统模型、架构模型和领域层代码模型的设计,最终影响到微服务的拆分和项目落地实施。
不要把与领域无关的业务逻辑放在领域层,避免领域业务逻辑被污染,保证领域层的纯洁,只有这样才能降低领域逻辑受外部变化的影响。在领域和架构模型建立后,代码模型的逻辑分层和微服务拆分要具体情况具体分析,根据自身研发和运维能力综合考虑。
(1) 项目级单应用
对于单应用系统的分层,遵循上述分层架构模型即可,核心领域逻辑在领域层实现,服务的组合和编排在应用层实现,两者组合形成中台,通过 API 对前台应用提供服务。
从部署和微服务拆分来讲,领域层代码部署时可能是一个微服务,也可能会根据限界上下文被拆分为多个微服务部署。应用层代码如果逻辑复杂,含较多个性业务逻辑,可以根据需要独立为微服务部署。如果逻辑简单,且领域层是一个微服务,在划分好应用层和领域层代码逻辑边界的情况下,如果符合微服务拆分原则,也可以考虑将应用层与领域层代码合并为一个微服务部署。
(2)企业级多中台应用
对于企业级多中台应用,多个中台应用通过 API 网关对外发布 API 服务。核心域业务中台在调用支撑域和通用域中台服务时通过核心域应用层完成多中台服务的组合和编排,为前台应用提供 API 服务。核心域中台的应用层是否独立成微服务部署,需考虑的情况与单应用系统相似。
应用层、领域层和基础设施层都有对应的服务,各司其职提供服务,其中基础设施层的服务通过依赖反转模式为领域层和应用层提供基础设施资源服务。应用层和领域层服务发布在 API 网关,通过 API 网关适配,为前台提供用户无差异化(应用 app、批处理或自动化测试)的服务。
由于上述架构模型中定义的外层只能依赖内层的架构原则,对于像数据库、缓存、文件系统等的外部基础设施资源,往往采用依赖反转的模式对外提供资源服务,实现应用层、领域层与基础设施层资源的解耦。在设计中应考虑资源层的代码适配逻辑,一旦基础设施资源出现变更(如换数据库),可以屏蔽资源变更对业务代码带来的影响,切断业务逻辑对基础资源的依赖,降低由于资源变更对业务逻辑的影响。
前台应用
从核心业务逻辑来看,中台实现了主要的业务逻辑,属于标准化的重量级应用。前台应用聚焦于界面交互以及业务流程等,属于轻量级应用,前台应用可以有个性的业务逻辑、流程和配置数据,甚至数据库,通过调用中台 API 服务完成交互界面和业务全流程。
在单机和集中式架构时代,系统分析和设计往往都是分阶段割裂进行的,容易导致需求、设计与代码实现的不一致,软件上线后才发现很多功能不是自己想要的,而且在这种模式下,软件也不能快速响应需求和业务变化。
领域驱动设计(DDD)打破了这种隔阂,它提出了领域模型概念,统一了分析、设计和开发语言和过程,使得软件能够更灵活快速响应需求变化。
软件分析和设计方法经历了三个阶段的演进:
领域驱动设计主要优势:1. 业务导向。2. 业务逻辑内聚,应用边界清晰。3. 建立领域模型优先。4. 分析、设计、代码和数据有机结合。5. 代码即设计。6. 扩展性好。
数据驱动设计主要特点:1. 技术导向。2. 数据库优先。3. 代码不能反映业务和设计。4. 业务逻辑分散。5. 扩展性不好。
2004 年 Eric Evans 发表《Domain-Driven Design –Tackling Complexity in the Heart of Software》 (领域驱动设计 )简称 Evans DDD。但在软件开发领域一直都是雷声大,雨点小,领域驱动设计核心思想是通过领域驱动设计方法定义领域模型,从而确定业务和应用边界,保证业务模型与代码模型的一致性。这几年之所以开始火起来,主要功劳要归功于队友“微服务”,领域驱动设计与微服务架构天生匹配。
领域驱动设计(DDD)是一种处理高度复杂域的设计思想,试图分离技术实现的复杂性,围绕业务概念构建领域模型来控制业务的复杂性,以解决软件难以理解,难以演化等问题。团队利用它可以成功的开发复杂业务软件系统,在系统变大时仍能保持敏捷性。
1.1 领域驱动设计分为两个阶段:
领域驱动设计包括战略设计和战术设计两个部分。战略设计主要关注按领域定义,在限界上下文内形成统一语言,提升业务和技术的沟通效率; 战术设计主要关注领域设计在落地时与设计模型及实现模型的差异性,减小业务和技术之间的鸿沟。(本文对 DDD 知识点不做详述,如需了解或学习,请查阅《领域驱动设计:软件核心复杂性应对之道》和《实现领域驱动》)。
1.2 领域驱动设计可能会给你带来以下收获:
领域驱动设计与微服务架构天生匹配,无论是在新项目中设计微服务架构,还是将系统从单体架构演进到微服务设计,都可以遵循领域驱动设计的架构原则。
1.3 为什么领域驱动设计是微服务架构的最佳设计方法?
领域驱动设计作为一种架构设计方法,微服务作为一种架构风格,两者从本质上都是为追求高响应力目标而从业务视角去分离复杂度的手段。 两者都强调从业务出发,其核心要义强调根据业务发展,合理划分领域边界,持续调整现有架构,优化现有代码,以保持架构和代码的生命力(演进式架构) 。
如果你的业务焦点在领域和领域逻辑,那么你就可以选择 DDD 进行微服务架构设计。
台的定义来源于阿里的中台战略(详见《企业 IT 架构转型之道:阿里巴巴中台战略思想与架构实战》钟华编著)。2015 年年底,阿里巴巴集团对外宣布全面启动阿里巴巴集团 2018 年中台战略,构建符合数字时代的更具创新性、灵活性的
“大中台、小前台”组织机制和业务机制,即作为前台的一线业务会更敏捷、更快速适应瞬息万变的市场,而中台将集合整个集团的运营数据能力、产品技术能力,对各前台业务形成强力支撑。
领域驱动设计中领域的定义:一个领域本质上可以理解为就是一个问题域,只要是同一个领域,那问题域就相同。所以只要我们确定了系统所属的领域,那这个系统的核心业务,即要解决的关键问题、问题的范围边界就基本确定了。领域的本质是问题域,问题域可能根据需要逐层细分,因此领域可分解为子域,子域或可继续分为子子域
在领域驱动设计中根据重要性与功能属性将领域分为三类子域,分别是:核心子域、支撑子域和通用子域。决定产品和企业独特竞争力的子域是核心子域,它是业务成功的主要因素和企业的核心竞争力。没有个性化的诉求,属于通用功能的子域是通用子域,如登陆认证。 还有一种所提供的功能是必须的,但不是通用也不是企业核心竞争力的子域是支撑子域,如单证。
2.1 DDD: 核心域、支撑域和通用域
中台、领域以及微服务属于不同层面的内容,稍作分解我们理清他们之间的关系。
案例分析
以保险领域为例,业务中台大致可分为两类:
专属业务中台是保险企业的核心竞争力,对应 DDD 的核心子域。通用中台对应 DDD 支撑子域和通用子域。不同领域可根据领域大小进一步细分多个子域,多个子域可对应到一个业务中台,一个业务中台也可能会分解成多个子域。
2.2 中台、领域以及微服务
DDD 设计包括战略设计和战术设计两个部分。在战略设计阶段主要完成领域建模和服务地图。在战术设计阶段,通过聚合、实体、值对象以及不同层级的服务,完成微服务的建设和实施。通过 DDD 可以保证业务模型、系统模型、架构模型以及代码模型的一致。
本部分主要讨论领域设计方法,如对战术设计和开发方法感兴趣可查阅 DDD 战术设计相关资料。
DDD 领域设计过程包括产品愿景、场景分析、领域建模和服务地图阶段,也可根据需要裁剪不必要的阶段和参与角色。领域驱动设计一般经历 2-6 周的时间,领域模型设计完成后,即可投入微服务实施。
3.1 DDD 分层架构中的服务
前面我们谈到了 DDD 的分层架构,分层架构主要包括:展现层、应用层、领域层和基础层(参考图:DDD(领域驱动设计)分层架构),各层都有不同的服务,但由于各层职责不一样,服务目的和实现方式也存在差异。
1、应用层服务
应用层是很瘦的一层,其服务主要用来表述应用和用户行为。它主要负责服务的组合、编排和转发,负责处理业务用例的执行顺序以及结果的拼装,拼装完领域服务后以粗粒度的服务通过 API 网关向前台应用发布。通过这样一种方式,隐藏了领域层的复杂性及其内部实现机制。 应用层除了定义应用服务之外,在这层还可以进行安全认证,权限校验,持久化事务控制或向其他系统发送基于事件的消息通知。
2、领域层服务
领域层是较“胖”的一层,它实现了全部业务逻辑并且通过各种校验手段保证业务正确性。业务逻辑包括:业务流程、业务策略、业务规则、完整性约束等。 当领域中的某个操作过程或转换过程不是实体或值对象的职责时,便将该操作放在一个单独的服务接口中,这就是领域服务,领域服务是无状态的。
3、基础设施层服务
基础设施层服务位于基础设施层,根据依赖倒置原则,封装基础资源服务,实现资源层与应用层和领域层的调用依赖反转,为应用层和领域层提供基础资源服务(如数据库、缓存等基础资源),实现各层的解耦,降低外部资源的变化对核心业务逻辑的影响。
4、总结
应用层服务是展现层和领域层的桥梁,通过调用领域对象和领域层服务来表达用例和用户故事。领域对象负责单一操作, 领域层服务用于协调多个领域对象共同完成某个业务操作。 应用服务原则上不处理业务逻辑,领域服务处理业务逻辑。
要做微服务而不是小单体
评判微服务设计合理的一个简单标准就是:微服务在随着业务发展而不断拆分或者重新组合过程中不会过度增加软件维护成本,并且这个过程是非常轻松且简单的。
3.2 微服务代码逻辑分层和结构
为了方便在微服务变大时实现快乐的拆分和合并,在明确各层代码职责后,我们需要对微服务代码合理分层和逻辑隔离,以下图为例对代码分层和结构进行简要说明。
代码逻辑分层和结构
对代码进行逻辑隔离和分层的主要意义在于:
1、避免各层代码的交叉,保持领域代码的纯洁,保证中台领域层业务逻辑的稳定。
2、业务和代码模型的逻辑保持一致,有利于微服务的拆分和组合。
微服务拆分过程中需严格遵守高内聚、低耦合原则,同时结合项目的实际情况,综合考虑业务领域、功能稳定性、应用性能、团队以及技术等因素。
1、基于业务领域拆分,在领域模型设计时需对齐限界上下⽂,围绕业务领域按职责单一性、功能完整性进行拆分,避免过度拆分造成跨微服务的频繁调用。
2、基于业务变化频率和业务关联拆分,识别系统中的业务需求变动较频繁的功能,考虑业务变更频率与相关度,并对其进行拆分,降低敏态业务功能对稳态业务功能的影响。
3、基于应用性能拆分,考虑系统⾮功能性需求,识别系统中性能压力较大的模块,并优先对其进行拆分,提升整体性能,缩小潜在性能瓶颈模块的影响范围。
4、基于组织架构和团队规模,提高团队沟通效率。
5、基于软件包大小,软件包过大,不利用微服务的弹性伸缩。
6、基于不同功能的技术和架构异构以及系统复杂度。
企业一旦采用分布式架构和微服务技术体系,在设计时需要关注商业模式、业务边界、数据体系、微服务设计、前台交互以及多活容灾等多领域的协同
5.1、数据是本难念的经
分布式架构下数据面临的问题远比集中式架构复杂。诸如:
(1)分布式数据库的选择
从集中式架构向分布式架构转型,第一步就需要考虑选择什么样的分布式数据库。
为解决交易型分布式数据库的横向计算能力,目前主要有三种类型的分布式数据库:
三者的使用场景基本相同,都是通过对大表数据作水平切分,业务请求动态路由到指定节点,以此达到计算能力的线性扩展。一体化方案是以数据库和中间件一体化产品的形式解决线性扩展问题,支持多副本,高可用,提供统一的运维界面。 数据库中间件方案是以独立数据库中间件结合集中式数据库的方式来解决线性扩展问题,高可用功能由中间件和数据库自身功能分别保证。分库基础类库方案是一种类似中间件的轻量级解决方案,适合简单快速的交易操作,在强一致性和聚合分析查询方面较弱。
(2)数据的分库和分库主键
选择完分布式数据库后,第二步就需要考虑如何按照领域模型和微服务进行数据库的分库设计,选择合适的分库主键将是一个关键技术点。
对于与客户接触的业务领域,个人认为可以以客户维度作为数据分库主键,以客户为实体,确保所有与本客户接触和服务的数据都在一个单元内,通过集中共享的中台服务,为所有渠道的客户提供一致性体验。如果后序管理流程需要基于区域管理要求,也可以考虑在后序业务环节的数据库中以区域维度作为数据库分库主键,满足业务基于区域的管理要求。
如何将客户维度的数据传输到以区域为维度的数据库中?我们可以考虑基于消息队列的事件驱动模型。
系统如果做不到“以客户为中心”,又如何能实现“以客户为中心”的业务需求呢?
(3)高频热点数据的缓存
对于像产品基础数据、主数据之类的热点高频访问数据,在进行系统设计时需考虑将这些数据加载至缓存中,降低数据库的压力,对外提供高性能的数据访问能力。
缓存技术的使用就像调味料一样,投入小见效快,用户体验提升快。
(4)数据副本与跨库联表查询
采用分布式技术后,数据将碎片化,为了减轻由于跨库以及联表查询给分布式数据库的压力,需要建立多维的全局数据视图(如客户统一视图、业务统计数据视图等)和面向具体场景的预处理好的数据聚合副本,提供复杂场景的数据查询服务,减轻交易型数据库的压力。
全局数据视图其数据来源于各业务条线的分布式数据库,从源端分布式数据库通过准实时的方式汇集(可以基于数据库日志捕获技术加消息队列)。全局视图的数据库也可以是分布式数据库,根据业务要求选择合适的分库主键进行数据重分布。
对于分布式数据库跨库关联查询性能低的问题,有两种解决方案,根据具体场景采用合适的方案:
(5)合理的数据冗余
完成领域模型和微服务设计后,集中式数据库的数据将被分散到不同微服务的分布式数据库中。数据实体的依赖关系将被打破,如果需要调用前序或后序微服务的数据实体(如:投保微服务生成的投保单、保单管理微服务的保单需要关联投保单,理赔的报案需要关联保单等,或电商业务中:销售过程中的商品、运输过程中的货物需要关联商品信息),这时候就会跨库或者跨微服务调用了,必然影响系统性能。
如何处理这些跨微服务的关键实体数据?
最好的方式就是数据冗余,将前序或后序环节的关键数据以数据清单复制表(只需必要的关键数据,不需要所有明细数据)的方式冗余存储。冗余的好处是,前台页面可以一次性获取本领域实体数据和关联实体清单数据,同时也可以在本库对关联清单数据进行查询。只有在需要获取关联实体数据明细时,才调用前序或后续微服务获取全量数据。
合理的数据冗余可以减少跨库查询,提升系统性能。
6)如何数据迁移?
从集中式数据库向分布式数据库切换时,数据迁移的复杂度将大大增加。需要考虑如何进行数据迁移?现有技术条件下,是不是不做数据迁移也可以无缝切换?
传统集中式架构数据多集中在一个集中式数据库中,数据关联度高。
分布式架构下,数据会随着微服务而同步拆分,数据将变得碎片化,存在复制表,数据重分布,数据关联被打破,甚至还可能需要重建数据关联。另外,分布式架构的容灾和多中心多活要求,数据迁移时还需要考虑数据的多副本和多中心的数据复制。分布式架构下数据迁移的复杂度大增。
互联网公司大多采用演进式架构模式,有计划分阶段的进行技术体系的升级,很多时候用户无感知就完成了架构的升级。而传统企业在做技术升级时如采用绞杀者重构模式,是否必须要做数据迁移?如果不做数据迁移是否也可以顺利切换?是否通过数据路由加全量数据视图的方案就可以不做数据迁移,实现新旧并存,无缝切换?数据切换方案需要详细设计和慎重考虑(尚在考虑中,且听下回分解)。
(7)数据的异步和同步
分布式架构下事件驱动设计模式是常用的方法,通过基于消息队列的发布订阅模式,可以很好的实现业务异步化。非实时业务场景可以采用事件驱动的模式实现异步化,减轻数据库压力。
也可以通过异步模式实现准实时的数据读写分离,提高数据库性能。
1、中台和微服务要处理好边界
条条道路通罗马,不管走哪条路,凭感觉或拍脑袋也可以设计出微服务,拆分结果可能与按照 DDD 方法出来的结果类似。但是如果有好的理论和方法指导,不但做事情有矩可循的,而且可以避免走弯路。由于 DDD 在设计的时候已经做好了逻辑的边界划分,在微服务需要组合和重新拆分时也会变得容易得多。
还是有必要提一下:中台和微服务设计可以借鉴 DDD 的设计原则和理念,不过战术设计部分由于过于复杂和学习成本过高,可以参考使用。
2、前、中台协同和前台数据的按需加载
前台应用未来可能多采用单页面(SPA)的微前端(对应于微服务的前端展现,一个微服务对应一个微前端)方式,通过前端集成框架(类似门户)实现多页面组合,提供统一的用户体验,在微服务和数据库设计时也需要协同考虑前端页面逻辑。
为减轻跨微服务的访问,前端页面展示时应以清单数据方式按需加载,后端数据设计时也应同步考虑如何组合前端数据展示。如需要展示明细数据,通过调用 API 服务的方式获取全量数据,减少不必要的跨微服务调用。
另外,符合条件的应用也可考虑页面的动静分离和路由接入,将静态页面通过 CDN 的技术,部署在靠近用户的机房,降低交互次数,减少跨广域网访问带来的网络延迟。
4、容灾和多活的全局考虑
分布式架构的高可用是在应用、数据和基础设施的分布式技术升级后,通过多数据中心协同来实现的。
为了容灾和多活,在设计方面需要考虑:1)合适的分布式数据库。2)合理的数据分库主键设计,数据的多副本和同步技术。3)单元化架构设计,处理好通用中台和专属中台的部署和依赖关系,实现业务的自包含,减少跨数据中心调用。4)访问层的接入,对外部访问进行路由、限流以及灰度发布。5)统一的全局配置数据,每个数据中心都有实时同步的全量配置数据,实现容灾和多活的一键切换。
5、避免过度拆分和硬件依赖
过度过细的微服务拆分带来更多的软件维护成本和运维压力,过多的分布式事务也会带来性能和数据一致性的压力。在进行设计时,要在保证逻辑边界清晰的情况下,严控微服务的过度拆分和采用过多的分布式事务。
分布式架构的自动的弹性伸缩大多是通过软件的方式去实现的,为保证应用的弹性伸缩能力,在设计中应实现去硬件的无中心化(如可采用软负载,就不用 F5 之类的硬负载),尽量通过软件实现弹性伸缩。因为一旦绑定硬件设备,在硬件遇到瓶颈需要自动弹性伸缩的时候,就需要人工干预,无法自动弹性伸缩。
相关文章
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。