写点什么

架构师训练第十周学习总结

用户头像
0x12FD16B
关注
发布于: 2020 年 08 月 12 日

在本周的课程中, 学习了微服务架构和领域驱动设计相关的知识。站在了更高的层次思考了分布式系统的架构和微服务架构, 了解了微服务架构的 "过去" (没有微服务架构之前, 系统的架构是怎么组织的), 现在的微服务架构的 "玩法" (现在市面上的系统如何利用微服务架构组织系统架构); 以及如何使用领域模型驱动设计去分析业务和指导微服务架构的设计。

重点知识整理

微服务

在没有微服务架构之前, 一个巨型的业务应用系统所面临的问题:

  • 编译、部署困难:功能组件多, 且依赖关系复杂, 编译耗时, 部署工作难度高; 复杂的组件依赖关系必然导致一个组件功能异常导致其他依赖方功能异常

  • 代码分支管理困难: 复用的代码模块由多个团队共同维护修改, 代码 merge 的时候难免发生冲突, 解决多个团队维护提交的代码的冲突会耗费大量的时间

  • 数据库连接耗尽: 巨型的应用、大量的访问,必然需要将这个应用部署在一个大规模的服务器集群上,应用与数据库的连接通常使用数据库连接池,以每个应用 10 个连接计算,一个数百台服务器集群的应用将需要在数据库连接上创建数千个连接,数据库服务器上,每个连接都会占用一些系统资源,以至于数据库缺乏足够的系统资源进行一般的数据操作

  • 新增业务困难:系统功能复杂, 在复杂的系统中新增功能, 难度大

解决上述困难的方案就是对系统进行拆分,将系统按照模块分解,将模块独立部署,降低系统耦合性,系统拆分的方法有:

  1. 纵向拆分:将一个大应用拆分为多个小应用,如果新增业务比较独立,那么就直接将其设计部署为一个独立的系统

  2. 横向拆分:将复杂的业务拆分出来,独立部署为微服务,新增业务只需要调用这些微服务即可快速搭建一个应用系统

微服务框架需求

对于大型互联网系统, 微服务框架应该具备服务注册与发现, 服务调用等标准功能, 还需要能够支持:

  • 失效转移 (Fail Over)

  • 对于大型网站的微服务而言, 即使是很少访问的简单服务, 也需要集群部署, 同时微服务框架还需要支持服务提供者的失效转移机制, 以实现服务高可用

  • 负载均衡

  • 对于集群部署的服务提供者, 服务请求者可以使用加权轮询等手段访问, 使服务提供者集群实现负载均衡

  • 高效的远程通信

  • 对于大型网站, 核心服务每天的调用次数会达到数以亿计, 如果没有高效的远程通信手段, 服务调用可能会成为整个系统性能的瓶颈

  • 对应用最少侵入

  • 网站技术是为业务服务的, 是否使用微服务需要根据业务发展规划, 微服务也需要渐进式的演化, 甚至会出现反复, 及时用了微服务后又退回到集中式部署, 微服务框架需要支持这种渐进式演化和反复。当服务模块本身需要支持可集中式部署,也可分布式部署。

  • 版本管理

  • 为了应对快速变化的需求,服务版本升级不可避免,如果仅仅是服务实现升级,那么这种升级对服务请求者而言是透明的,无需关注。但是如果服务的访问接口发生变化,就需要服务请求者和服务提供者同时升级才不会导致服务调用失败。企业应用系统可以申请停机维护,同时升级接口,而网站服务不可能中断,需要服务提供者先升级接口,并同时提供历史版本的服务请求者调用,当请求者访问接口升级后才可以关闭历史版本服务。

微服务框架 (Dubbo) 框架





上图为使用 dubbo 框架进行微服务调用的应用通讯示意图。

在课上同时还讲解了 dubbo 协议请求的设计。

微服务架构实践

微服务架构落地

  • 业务先行, 先理顺业务边界和依赖, 技术是手段而不是目的

  • 先有独立的模块, 后有分布式的服务

  • 如果业务耦合严重, 逻辑复杂多变的系统进行微服务重构需要谨慎

  • 要搞清楚实施微服务的目的是什么

命令与查询职责隔离 (CQRS)

在服务接口层面将查询 (读操作) 与命令 (写操作) 隔离, 实现服务层的读写分离。CQRS 模式能带来如下的好处:

  • 更清晰的领域模型

  • 针对读写分别优化,实现更好的性能

  • 查询服务不会修改数据,更好地保护数据

事件溯源

将用户请求处理过程中的每次状态变化都记录到事件日志中, 并按时间序列进行持久化存储。这样做能带来如下的好处:

  • 利用事件溯源, 可以精确复现任何用户状态,进行复核审计

  • 利用时间溯源,可以有效监控用户状态变化,并可以在此基础上实现分布式事务

断路器

当某个服务出现故障, 响应延迟或者失败率增加, 继续调用这个服务会导致调用者请求阻塞, 资源消耗增加, 进而出现服务级联失效, 这种情况下使用断路器阻断对服务的调用来防止服务级联失效。

服务重试及调用超时

上游调用者超时时间要大于下游调用者超时时间之和

最重要的是需求

理清楚需求, 整理出业务领域的模型是关键。

领域驱动设计

为什么需要 DDD

很多项目的实际情况是:

  • 用户或产品经理的需求零零散散, 不断变更

  • 工程师在各处代码中寻找可以实现这些需求变更的代码, 修修补补

  • 软件只有需求分析, 并没有真正的设计, 系统没有一个统一的领域模型维持其内在的逻辑一致性

  • 功能特性并不是按照领域模型内在的逻辑设计, 而是按照各色人等自己的主观想象设计

项目事件一长, 各种困难重重, 需求不断延期, 线上 BUG 不断, 管理者考虑是不是要推倒重来, 而程序员则考虑是不是要跑路

事务脚本

目前企业级应用开发中,业务逻辑的组织方式主要是事务脚本模式。事务脚本按照业务处理的过程组织业务逻辑,每个过程处理来自客户端的单个请求。客户端的每次请求都包含了一定的业务处理逻辑,而程序则按照每次请求的业务逻辑进行划分。事务脚本模式典型的就是 Controller → Service → Dao 这样的程序设计模式。Controller 封装用户请求,根据请求参数构造一些数据对象调用 Service,Service 里面包含大量的业务逻辑代码,完成对数据的处理,期间可能需要通过 Dao 从数据库中获取数据,或者将数据写入数据库中。由于事务脚本模式中,Service、Dao 这些对象只有方法,没有数值成员变量,而方法调用时传递的数值对象没有方法(或者只有一些 getter、setter 方法),因此事务脚本又被称作贫血模型。

领域模型

领域模型模式和事务脚本模式不同。在领域模型模式下,业务逻辑围绕领域模型设计。领域模型是合并了行为和数据的领域的对象模型。通过领域模型对象的交互完成业务逻辑的实现,也就是说,设计好了领域模型对象,也就设计好了业务逻辑实现。和事务脚本被称作贫血模型相对应的,领域模型也被称为充血模型。

对于复杂的业务逻辑实现来说,用领域模型模式更有优势。特别是在持续的需求变更和业务迭代过程中,把握好领域模型,对业务逻辑本身也会有更清晰的认识。使用领域模型增加新的产品类型的时候,就不需要修改现有的代码,只需要扩展新的产品类和收入策略类就可以了。

领域

领域是一个组织所做的事情以及其包含的一切,通俗地说,就是组织的业务范围和做事方式,也是软件开发的目标范围。领域驱动设计就是从领域出发,分析领域内模型及其关系,进而设计软件系统的方法。

子域

领域是一个组织所做的事情以及其包含的一切,那么这个范围就太大了,不知道该如何下手。所以通常的做法是把整个领域拆分成多个子域,比如用户、商品、订单、库存、物流、发票等。

界限上下文

在一个子域中, 会创建一个概念上的领域边界, 在这个边界中, 任何领域对象都只表示特定于该边界内部的确切含义。这样的边界称为界限上下文。界限上下文和子域有一一对应的关系,用来控制子域的边界。通常界限上下文对应一个组件或者一个模块,或者一个微服务。

上下文映射图

不同的界限上下文,也就是不同的子系统或者模块之间会有各种的交互合作。DDD 使用上下文映射图来完成这种交互。

实体

领域模型对象也被称为实体,每个实体都是唯一的,具有一个唯一标识,一个订单对象是一个实体,一个产品对象也是一个实体,订单 ID 或者产品 ID 是它们的唯一标识。实体可能会发生变化,比如订单的状态会变化,但是它们的唯一标识不会变化。实体设计是 DDD 的核心所在,首先通过业务分析,识别出实体对象,然后通过相关的业务逻辑设计实体的属性和方法。这里最重要的,是要把握住实体的特征是什么,实体应该承担什么职责,不应该承担什么职责,分析的时候要放在业务场景和界限上下文中,而不是想当然地认为这样的实体就应该承担这样的角色。

值对象

并不是领域内的对象都应该被设计为实体,DDD 推荐尽可能将对象设计为值对象。比如像住址这样的对象就是典型的值对象,也许建在住址上的房子可以被当做一个实体,但是住址仅仅是对房子的一个描述,像这样仅仅用来做度量或描述的对象应该被设计为值对象。值对象的一个特点是不变性,一个值对象创建以后就不能再改变了。如果地址改变了,那就是一个新地址,而一个订单实体则可能会经历创建、待支付、已支付、代发货、已发货、待签收、待评价等各种变化。

聚合

聚合是一个关联对象的集合, 我们将其作为一个单元来处理数据更改。每个集合都有一个根和一个边界。边界定义了聚合内部的内容。根是聚合中包含的单个特定实体。聚合根是将多个实体和值对象聚合在一起的实体。

DDD 分层架构

领域实体被放置在领域层,通过应用层对领域实体进行包装,最终提供一组访问接口,通过接口层对外开放。领域实体的组合调用和事务控制在应用层。





DDD 战略设计与战术设计

领域、子域、界限上下文、上下文映射图,这些是 DDD 的战略设计。

实体、值对象、聚合、CQRS、事件溯源,这些是 DDD 的战术设计。

通过战略设计,划分模块和服务的边界及依赖关系,对微服务架构的设计至关重要。

学习感悟

这周的学习之后, 最大的感悟就是架构的发展, 和架构设计指导的方法学目的其实本质上还是以设计出可用性, 可维护性强的系统,各种设计方法只是指导我们往那个方向走,所以正如这个训练营开篇的时候讲的那样,最重要还是得洞察问题的本质。



用户头像

0x12FD16B

关注

还未添加个人签名 2018.01.19 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练第十周学习总结