架构师训练营第十周课程笔记及心得

用户头像
Airs
关注
发布于: 2020 年 11 月 29 日

微服务:服务本身的设计、维护以及治理



早期单体系统的架构:



巨大的单体系统带来的问题:



  1. 编译、部署困难

  2. 代码分支管理困难

  3. 数据库连接耗尽

  4. 新业务困难





解决方案:



拆分,将模块独立部署,降低耦合性



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

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

  3. 但是近些年,很多系统做微服务变成了把大系统直接做成微服务模式,反而没有实现微服务的能力,只是让系统变的更加复杂,原因就是作微服务的过程中没有合理里的进行拆分





微服务框架:



Web Service与企业级分布式服务SOA





缺陷:



  1. 臃肿的注册与发现机制

  2. 低效的XML序列化手段

  3. 开销相对较高的HTTP远程通信

  4. 复杂的部署与维护手段

  5. 这些问题导致了WebService难以满足大型网站对系统高性能、高可用、易部署、易维护的要求



微服务框架需求:



对于大型互联网系统,除了WebService所谓反的服务注册于发现,服务调用等标准功能,还需要微服务框架能够支持:



  1. 负载均衡:对于集群部署的服务提供者,服务请求者可以使用甲醛轮询灯手段访问,使服务提供者集群实现负载均衡

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

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

  4. 对应用最少侵入:网站技术是为业务服务的,是否使用微服务需要根据业务发展规划,微服务也需要渐进式的烟花,甚至会出现反复,即使用了微服务后又退回到集中式部署,微服务框架需要支持这种渐进式演化和反复。淡然服务模块本身需要支持可集中式部署,也可分布式部署。

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



微服务框架(Dubbo)架构:





微服务:落地实践的策略与思路



Service Mesh 服务网格:



Service Mesh是一个基础设施层,用于处理服务间的通信,通常表现为一组轻量级网络代理,他们与应用程序部署在一起,而对应用程序透明





Service Mesh的Sidecar模式:







微服务架构实践落地:



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

  2. 现有独立的模块,后有分布式的服务。

  3. 业务耦合严重,逻辑复杂多变的系统进行微服务重构要谨慎。

  4. 要搞清楚实施微服务的目的是什么,业务复用?开发边界清晰?分布式集群提升性能?



一些其他的微服务的概念



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



在服务接口层面将查询(读操作)与命令(写操作)隔离,实现服务层的读写分离



  1. 更清晰的领域模型

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

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





事件溯源:



将用户请求处理过程中的每次状态变化都记录到事件日志中,并按时间序列进行持久化存储



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

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



断路器:



断路器三种状态:关闭、打开、半开:





服务重试及调用超时:



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





最重要的是需求:



重要程度:需求-价值-原则-实践-工具





微服务网关的技术架构:



基于网关的微服务架构:





网关的作用:



  1. 统一接入

  2. 安全防护

  3. 协议适配

  4. 流量管控与容错





微服务网关:



使用了微服务网关之后,结构变成了:





网关管道技术:



网关本身没有什么业务,主要职责是做各种校验与拦截,这些职责可以通过管道技术连接起来。





实现管道技术的责任链设计模式





Flower异步网关与异步微服务框架:





利用servlet3实现异步网关:





开放平台网关:



  1. API接口:是开放平台暴露给合作者使用的一组API,其形式可以是RESTful、WebService、RPC等各种形式

  2. 协议转换:将各种API输入转换成内部服务可以识别的形式,并将内部服务的返回封装成API的格式

  3. 安全:除了一般应用需要的身份识别、权限控制等安全手段,开放平台还需要封机的访问带宽限制,保证平台资源被第三方应用公平合理使用,也保护网站内部服务不会被外部应用拖垮

  4. 审计:记录第三方应用的访问情况,并进行监控、计费等

  5. 路由:将开放平台的各种访问路由映射到具体的内部服务

  6. 流程:将一组离散的服务组成一个上下文相关的新服务,隐藏服务细节,提供统一接口供开发者调用





开放授权协议OAuth2.0





授权码授权方式:



OAuth2.0一共有四种授权方式,分别是授权码、隐式授权、资源所有者密码凭据和客户端凭据,目前使用最多的是授权码方式





领域驱动设计DDD



为什么需要DDD:



很多项目的实际情况:



  1. 用户或者产品经理的需求零零散散,不断变更。

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

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

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

  5. 由上面这些因素,项目时间一长,各种困难重重,需求不断延期,线上bug不断,管理者考虑是不是要推倒重来,而程序员则考虑是不是要跑路



事务脚本:



原始的业务实现模型:





领域模型:



利用领域驱动实现的业务模型把service拆分开来,通过领域分析来拆分对象类,每个对象承担自己的业务职责和业务逻辑,通过对象之间的组合方式,完成业务功能





贫血模型vs充血模型



  1. 贫血模型:由于事务脚本模式中,Service、Dao这些对象只有方法,没有数值成员变量,而方法调用时传递的数值对象,比如Contract,没有方法(或者只有一些getter、setter方法),因此事务脚本又被称作贫血模型。

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



DDD战略设计



领域是一个组织所做的事情以及其包含的一切,通俗的说,就是组织的业务范围和做事方式,也是软件开发的目标范围。



领域驱动设计就是从领域出发,分析领域内模型及其关系,进而设计软件系统的方法。





子域:



领域是一个组织所做事情以及包含的一切。这么想的话范围会很大,不方便着手。所以通常的做法是把整个领域拆分成多个子域,比如用户、商品、订单、库存、物流、发票等。如何划分子域:



限界上下文:



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



上下文映射图:



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



实体:



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



值对象:



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



聚合:



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



聚合根:



将多个实体和值对象聚合在一起的实体。是微服务的最小粒度





DDD分层架构:



领域实体的组合调用和事务控制在应用层





DDD六边形架构:



领域模型通过应用程序封装成一个相对比较独立的模块,而不同的外部系统则通过不同的适配器和领域模型交互,比如可以通过HTTP接口访问领域模型,也可以通过WebService或者消息队列访问领域模型,只需要为这些不同的访问接口提供不同的适配器就可以了。





DDD战略设计与战术设计:



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

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

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



软件组件设计原则



软件的复杂度和它的规模成指数关系



一个复杂度为100的软件系统,如果能拆分成两个互不相关、同等规模的子系统,那么每个子系统的复杂度应该是25,而不是50。软件开发这个行业很久之前就形成了一个共识,应该将复杂的软件系统进行拆分,拆成多个更得复杂度的子系统,子系统还可以继续拆分成更小粒度的组件。也就是说,软件需要经行模块化、组件化设计。



软件组件设计也是遵循高内聚低耦合的原则



组件内聚原则:



组件内聚原则主要讨论哪些类应该聚合在同一个组件中,以便组件技能提供相对完整的功能,又不至于太过庞大



  1. 复用发布等同原则:软件复用的最小粒度应该等同于其发布的最小粒度。也就是说,如果你希望别人以怎样的粒度复用你的软件,你就应该以怎样的粒度发布你的软件。这其实就是组件的定义了,组件是软件复用和发布的最小粒度软件单元。这个粒度既是复用粒度,也是发布的粒度

  2. 共同封闭原则:我们应该将那些会同时修改,并且为了相同目的而修改的类放到同一个组件中。而将不会同时修改,并且不会为了相同目的而修改的类放到不同的组件中。

  3. 共同复用原则:共同复用原则是说,不要强迫一个组件的用户依赖他们不需要的东西。



组件耦合原则:



组件耦合性原则讨论组件之间的耦合关系应该如何设计



  1. 无循环依赖原则:组件依赖关系中不应该出现环。如果组件A依赖组件B,组件B依赖组件C,组件C又依赖组件A,就形成了循环依赖。

  2. 稳定依赖原则:组件不应该依赖一个比自己还不稳定的组件

  3. 稳定抽象原则:组件的抽象化程度应该与其稳定性程度一致,也就是说,一个稳定的组件应该是抽象的,而不稳定的组件应该是具体的。



组件的边界与依赖关系,不仅仅是技术问题



组件的边界与依赖关系划分,不仅需要考虑技术问题,也要考虑业务场景问题。易变与稳定,依赖与被依赖,都需要放在业务场景中去考察。有的时候,甚至不只是技术和业务的问题,还需要考虑人的问题,在一个复杂的组织中,组件的依赖与设计需要考虑人的因素,如果组件的功能划分涉及到部门的职责边界,甚至会和公司内的整治关联起来。康威定律:组织架构决定了系统架构



DDD重构实践过程(案例):



  1. 当前系统设计与问题汇总讨论

  2. 针对问题分析具体原因

  3. 中心梳理业务规则和边界,明确业务术语

  4. 技术框架选型与落地方案验证

  5. 任务分解与持续重构



发布于: 2020 年 11 月 29 日阅读数: 22
用户头像

Airs

关注

Emmmmmmm 2018.02.28 加入

Emmmmmmm

评论

发布
暂无评论
架构师训练营第十周课程笔记及心得