第 10 周总结:微服务,DDD
微服务框架
大型互联网系统,对微服务框架的要求:
失效转移(Fail Over):对于大型网站的微服务而言,即使是很少访问的简单服务/也需要集群部署,同时微服务框 架还需要支持服务提供者的失效转移机制,以实现服务高可用。
负载均衡:对于集群部署的服务提供者,服务请求者可以使用加权轮询等手段访问,使服务提供者集群实现负载均衡。
高效的远程通信:对于大型网站,核心服务每天的调用次数会达到数以亿计,如果没有高效的远程通信手段, 服务调用可能会成为整个系统性能的瓶颈。
对应用最少侵入:随着业务的发展,微服务框架药要能支持渐进式演化和反复。
版本管理:为了应对快速变化的需求,服务版本升级不可避免,但是网站服务不可能中断,需要服务提供者先升级接口,并同时提供历史版本的服务供请求者调用,当请求者访问接口升级后才可以关闭历史版本服务。
Service Mesh是一个基础设施层,用于处理服务间的通信,通常表现为一组轻量级网络 代理’他们与应用程序部署在一起,而对应用程序透明。
Service Mesh 的Sidecar 模式。
微服务架构落地需要注意的地方
业务先行,先理顺业务边界和依赖,技术是手段而不是目的。
先有独立的模块,后有分布式的服务。
业务耦合严重’逻辑复杂多变的系统进行微服务重构要谨慎。
要搞清楚实施微服务的目的是什么,业务复用?开发边界清晰?分布式集群提升性能?
CQRS(命令与查询职责隔离)是什么?
CQRS指的是在服务接口层面将查询(读操作)与命令(写操作)隔离,实现服务层的读写分离
CQRS的优点
更清晰的领域模型
针对读写分别优化,实现更好的性能
查询服务不会修改数据,更好地保护数据
事件溯源:将用户请求处理过程中的每次状态变化都记录到事件日志中,并按时间序列进行持久化存储。
事件溯源的好处
利用事件溯源,可以精确复现任何用户状态,进行复核审计。
利用事件溯源,可以有效监控用户状态变化,并在此基础上实现分布式事务。
断路器:当某个服务出现故障,响应延迟或者失败率增加,继续调用这个服务会导致调用者请求 阻塞,资源消耗增加,进而出现服务级联失效/这种情况下使用断路器阻断对故障服务的调用。
断路器三种状态:关闭,打开,半开。
服务重试及调用超时
上游调用者超时时间要大于下游调用者超时时间之和。
最重要的是需求。
基于网关的微服务架构。
网关的作用
统一接入:高性能、高并发、高可靠、负载均衡
安全防护:防刷控制、黑白名单
流量管控与容错:限流、降级、馅断
协议适配:http、dubbox jsf
网关管道技术
网关本身没有什么业务,主要职责是做各种校验与拦截,这些职责可以通过管道技术连 接起来。
实现管道技术可以用责任链设计模式
Flower异步网关与异步微服务框架。
利用Servlet3实现异步网关。
开放平台网关。
开放授权协议OAuth2.0。
OAuth2.0 一共有四种授权方式, 分别是授权码、隐式授权、资源 所有者密码凭据和客户端凭据。
目前互联网上使用最多也是最安全的的一种方式是授权码方式。
领域驱动设计DDD
事务脚本 VS 领域模型
贫血模型 VS 充血模型
由于事务脚本模式中, Service. Dao这些对象只有方法,没有数值成员变量,而方法调 用时传递的数值对象/比如Contract/没有方法(或者只有一些getter、setter方法)/ 因此事务脚本又被称作贫血模型。
领域模型是合并了行为和数据的领域的对象模型。通过领域模型对象的交互完成业务逻辑的实现,也就是说,设计好了领域模型对象,也就设计好了业务逻辑实现。和事务脚 本被称作贫血模型相对应的,领域模型也被称为充血模型。
领域
领域是一个组织所做的事情以及其包含的一切。通俗地 说,就是组织的业务范围和 做事方式/也是软件开发的目标范围。
领域驱动设计就是从领域出发,分析领域内模型及其关系‘进而设计软件系统的方 法。
子域
领域是一个组织所做的事情以及其包含的一切。这个范围就太大了,不知道该如何下手。所以通常的做法是把整个领域拆分成多个子域,比如用户、商品、订单、库存、物流、
发票等。
在一个子域中,会创建一个概念上的领域边界,在这个边界中,任何领域对象都只表示 特定于该边界内部的确切含义。这样边界便称为限界上下文。限界上下文和子域具有一 对一的关系,用来控制子域的边界。
通常限界上下文对应一个组件或者一个模块/或者一个微服务。
不同的界限上下文/也就是不同的子系统或者模块之间会有各种的交互合作。DDD使用上下文映射图来设计这种交互。
实体
领域模型对象也被称为实体/每个实体都是唯一的/具有一个唯一标识/—个订单对象是一个实体/—个产品对象也是一个实体,订单ID或者产品ID是它们的唯一标识。实体可能会发生变化,比如订单的状态会变化,但是它们的唯一标识不会变化。
实体设计是DDD的核心所在,首先通过业务分析,识别出实体对象,然后通过相关的 业务逻辑设计实体的属性和方法。这里最重要的/是要把握住实体的特征是什么/实体 应该承担什么职责,不应该承担什么职责,分析的时候要放在业务场景和界限上下文中, 而不是想当然地认为这样的实体就应该承担这样的角色。
值对象
并不是领域内的对象都应该被设计为实体, DDD推荐尽可能将对象设计为值对象。比如像住址这样的对象就是典型的值对象,也许建在住址上的房子可以被当做一个实体/但 是住址仅仅是对房子的一个描述,像这样仅仅用来做度量或描述的对象应该被设计为值 对象。
值对象的一个特点是不变性,一个值对象创建以后就不能再改变了。如果地址改变了,
那就是一个新地址,而一个订单实体则可能会经历创建、待支付、已支付、代发货、已
发货、待签收、待评价等各种变化。
聚合是一个关联对象的集合/我们将其作为一个单元来处理数据更改。每个集合都有一个根和一个边界。边界定义了聚合内部的内容。根是聚合中包含的单个特定实体。
聚合根:将多个实体和值对象聚合在一起的实体。
DDD战略设计与战术设计
领域、子域、界限上下文、上下文映射图/这些是DDD的战略设计。
实体、值对象、聚合、CQRS、事件溯源,这些是DDD战术设计。
通过战略设计/划分模块和服务的边界及依赖关系/对微服务架构的设计至关重要。
资深程序员真正有优势的是他在一个业务领域的多年积淀,对业务领域有更深刻的理解和认知。那么如何将这些业务沉淀和理解反映到工作中,体现在代码中呢?实践DDD 是一个不错的方式。
如果一个人有多年的领域经验,那么必然对领域模型设计有更深刻的认识,把握好领域 模型在不断的需求变更中的演进,使系统维持更好的活力/并因此体现自己真正的价值。
软件的复杂度和它的规模成指数关系
一个复杂度为100的软件系统,如果能拆分成两个互不相关、同等规模的子系统,那么每个子系统的复杂度应该是25,而不是50。软件开发这个行业很久之前就形成了一个共识/应该将复杂的软件系统进行拆分,拆成多个更低复杂度的子系统,子系统还可以 继续拆分成更小粒度的组件。也就是说/软件需要进行模块化、组件化设计。
组件内聚原则:组件内聚原则主要讨论哪些类应该聚合在同一个组件中,以便组件既能提供相对完整的功能,又不至于太过庞大。
复用发布等同原则:软件复用的最小粒度应该等同于其发布的最小粒度。也就是说, 如果你希望别人以怎样的粒度复用你的软件,你就应该以怎样的粒度发布你的软件。这 其实就是组件的定义了/组件是软件复用和发布的最小粒度软件单元。这个粒度既是复用的粒度,也是发布的粒度。
共同封闭原则:我们应该将那些会同时修改,并且为了相同目的而修改的类放到同 一个组件中。而将不会同时修改,并且不会为了相同目的而修改的类放到不同的组件中。
共同复用原则是说,不要强迫一个组件的用户依赖他们不需要的东西。
组件耦合原则:组件内聚原则讨论的是组件应该包含哪些功能和类,而组件耦合原则讨论组件之间的耦合关系该如何设计。
无循环依赖原则:组件依赖关系中不应该出现环。如果组件A依赖组件B组件B依赖组件C,组件C又依赖组件A,就形成了循环依赖。
稳定依赖原则: 通俗地说就是组件不应该依赖一个比自己还不稳定的组件
稳定抽象原则: 这个原则对具体开发的指导意义是,如果你设计的组件是具体的、不稳定的,那么可以为这个组件对外提供服务的类设计一组接口,并把这组接口封装在一个专门的组件中, 那么这个组件相对就比较抽象、稳定。
组件的边界与依赖关系,不仅仅是技术问题。
RPC协议实现原理
通讯协议在电信领域中是指在任何物理介质中允许两个或多个在传输系统中的终端之间传播信息的系统标准,也是指计算机通信或者网络设备的共同语言。
一个完整的应用层通信协议通常包含两个部分
网络通信协议:TCP、UDP
编码传输协议:二进制、文本,协议头格式
设计私有通讯协议的好处
充分并有效利用通讯协议里的每个字段,减少冗余数据传输
灵活满足自定义通讯需求,例如CRC校验、Server Fail-fast、自定义序列化器
最大程度满足性能需求:10模型和线程模型的充分利用
评论