阿里早期为什么要上微服务?
单体应用的问题
- 一个war包里包含所有业务模块
- 打包的war包很大,编译,部署困难
- 代码分支管理困难
- 数据库连接耗尽
- 新增模块困难
面向服务器的架构思想,最早应用的是webService,因为以下缺点导致没有在互联网项目中大规模应用:
- 臃肿的注册与发现机制
- 低效的XML序列号手段
- 开销相对较高的Http远程通信
- 复杂的部署与维护手段
微服务框架的需求
- 服务注册与发现
- 失效转移
- 负载均衡
- 高效的远程通信
- 对应用最少侵入
- 应用可以无感知地调用远程服务。
- 接口的版本管理
微服务架构
Dubbo的架构
- 服务器提供者
- 在服务的容器中启动
- 服务容器根据配置把服务列表向服务中心提供
- 收到消费者的请求后,将结果打包通过远程模块返回给消费者
- 服务注册中心
- 维护服务器与服务列表的对应关系
- 服务消费者
- 通过服务接口调用服务,不依赖具体的实现
- 服务接口通过接口代理访问服务框架客户端
- 客户端先访问本地的服务提供列表,如果没有再访问远程的服务注册中心
- 通过自己的负载均衡模块确定要连接的具体ip和端口
- 通过远程通讯模块连接到远程服务的ip和端口,一般会使用tcp的长连接
- 服务框架客户端打包请求,通过远程通讯模块发送给服务提供者
- 接口访问代理将收到的结果包反序列化成class类,供服务接口调用
Service Mesh 服务网格
- 是一个基础设施层,用于处理服务间的通信
- 通常表现为一组轻量级的网络代理,与应用程序部署在一起,而对应用程序透明
service Mesh的边车模式(sideCare模式)
更多的service Mesh可以连成一个网络
微服务实践
微服务如何落地
- 业务先行,业务是最重要的,如果没有理清楚业务边界和依赖,很容易导致失败
- 业务耦合严重,逻辑复杂多变的系统进行微服务重构要谨慎
- 技术是手段不是目的
- 先有独立的模块,然后才有分布式的服务
- 要搞清楚实施微服务的目的是什么,不能为了微服务而做微服务。确定了目的在业务拆分的时候才能更好的做出选择,一个业务到底应该怎么拆分。
事件溯源
完整记录处理过程中的每次状态编号,并按时间序列进行持久化存储。
- 可以精确复现任何用户状态,进行复核审计
- 可以有效监控用户状态编号,在此基础上实现分布式事务。
- 可以方便的设置检查点,判断要做commit或rollback
断路器
当某个服务出现故障,响应延迟或者失败率增加,继续调用这个服务就会导致调用者请求阻塞,资源消耗增加,进而出现服务级联失效,这时应该使用断路器对故障服务进行调用。
三种状态:关闭、打开,半开
服务重试及调用超时
上游调用者超时时间要大于下游调用者超时时间之和,所以越往下的调用者超时时间应该越短
要关注的问题排序
需求是最重要的,用来解决什么问题
能否带来期望的价值
原则
最佳实践
选用工具
RPC协议实现原理
本质还是方法调用,但是是远程的
要解决的问题:
- 怎样进行远程通信
- 怎样找到要调用的那个方法
通讯协议
通信协议:tpc、ucp
编码传输协议:二进制、文本、json,xml
高效的通讯协议需要自定义,以充分利用协议中的每个字段,避免数据冗余
常见的私有协议模式
- 定长协议
- 协议内容长度是固定的,读写性能高,但灵活性差
- 特殊结束符协议
- 用户数据中不能包含结束符否则会解析错误,且需求读取所有数据包后才能进行处理
- 变长协议
- 使用更广泛
- 协议头+协议体
- 使用固定长度的协议头记录协议体特征
Dubbo协议
- 大部分的网关都不是异步的,如果要实现异步可以利用servlet3实现
开发平台网关
一般要考虑实现的功能包括:
- API接口
- 协议转换
- 安全
- 除了一般应用需要的身份识别、权限控制等安全手段
- 还需要实现分级的访问带宽限制,保证平台服务不会受到外部服务调用的影响
- 审计
- 记录第三方应用的访问情况,在此基础上实现计费,监控等功能
- 路由
- 将各种访问路由映射到具体的内部服务
- 流程
- 将一组离散的服务组织成一个上下文相关的新服务,隐藏服务细节,提供统一接口给第三方调用
- 开放授权协议OAuth2.0
- 授权码授权示例
- 授权码授权示例
领域驱动设计DDD
DDD是为了实现领域模型的一种方法和手段
- 为什么需要DDD
- 需求零零散散,不断变更
- 实现需求的代码有多种方式可以放在这个模块,也可以放到别的模块,没有一个统一的模型维持其内在的逻辑一致性
- 领域模型要维持业务一致、产品一致、内部实现一致
- 代码实现要做到优雅,逻辑自洽
领域是什么
领域是一个组织所做的事情以及其所包含的一切。即组织的业务范围和做事方式,也是软件开发的目标范围。
领域驱动设计就是从领域出发,分析领域内模型及其关系,进而设计软件系统的方法。
传统的做法
- 事务脚本,随着业务的增加,会导致service层增大,业务逻辑复杂
- 也叫贫血模式,Service、dao中只有方法,没有数值成员变量;而方法调用时传递的数值,只有数据,没有方法;
领域模型
- 每个类复杂所有和自己相关的操作,比如Contract的计算是没有参数的,内部实现都是通过自己的成员变量来计算所得,具体成员变量的值则从外部获取,而不是将数据和操作分离开。
- 类似于面向对象的设计方式
- 又称为充血模式,相比贫血模式,领域模型的对象既包含对象的数据,又包含了计算逻辑。
- 设计好了领域模型,也就等于设计好了业务逻辑。
DDD的战略设计
拆分子域
领域包含的范围过大,通常的做法是首先把领域拆分成子域,比如用户、商品、订单、库存、物流、发票等子域。
子域实际上就对应微服务。
限界上下文
在一个子域中,会创建一个概念上的领域边界。在这个边界内,任何领域对象都只表示特定于该边界内部的确切含义。这样的边界便称为限界上下文。
限界上下文和子域具有一对一的关系,用来控制子域的边界,保证子域内的概念统一。
比如商品在不同的子域(比如财务,订单,物流等)内代表不同的模型和业务流程,要在所有子域内实现统一并不现实,所以要限定上下文,保证在一个子域内商品的定义是一致的。
通常限界上下文对应一个组件或一个模块,或者一个微服务,一个子系统。
上下文映射图
不同的限界上下文会有各种交互,DDD中使用『上下文映射图』来设计这种关联和交互。
DDD的战术设计
实体
- 领域模型的对象也被称为实体
- 每个实体都是唯一的,具有一个唯一标识
- 实体可能发生变化,但唯一标识是不会变的
- 实体设计是DDD的核心所在
- 通过业务分析,识别出实体对象
- 通过业务逻辑设计实体的属性和方法
- 要把握实体的特征是什么
- 应该承担什么职责
- 不应该承担什么职责
- 分析的时候要把实体放到业务场景和限界上下文中,而不是考虑通常意义上的实体,想当然的觉得应该承担什么职责
值对象
- 并不是领域内的所有对象都应该被设计成实体,DDD推荐尽可能地将对象设计为值对象。
- 什么样的对象应该是值对象
- 有不变性
- 只是对一个实体的描述或者度量
- 比如住址
- 是对房子这个实体的描述
- 有不变性,如果住址变了,那就需要创建一个新的住址对象
- 与之相对的比如订单,订单的状态会经历创建,待支付,已支付,已发货等变化,当仍然是同一个订单
聚合
- 聚合是一些关联对象的集合,我们将其作为一个单元来处理数据更改
- 每个集合都有一个根和一个边界。
- 边界定义聚合内部的内容
- 根是聚合包含的单个特定实体
- 聚合根,将多个实体和值对象聚合在一起的实体,负责对外提供服务。外部如果要调用方法应该通过聚合根来实现,而不是调用其内部的对象。(比如车对象是聚合根,启动应该调用车的启动实现,而不是调用车引擎的启动来实现)。
命令与查询职责隔离(CQRS)
在服务层面实现读写分离,可以分别针对读写进行优化,是使用缓存还是消息队列
事件溯源
完整记录处理过程中的每次状态编号,并按时间序列进行持久化存储。
- 可以精确复现任何用户状态,进行复核审计
- 可以有效监控用户状态编号,在此基础上实现分布式事务。
- 可以方便的设置检查点,判断要做commit或rollback
DDD的分层设计
领域层中包含各种实体以及实现
领域实体的组合和调用,应该控制在应用层
用户接口层通过应用层调用领域层实现业务逻辑
DDD六边形架构
领域模型通过应用程序被封装成相对比较独立的模块,而不同的外部系统有不同的业务需求。
比如外部系统有的需要通过Http接口访问领域模型,有的需要通过Web Service接口访问模型,这时要做的就是为领域模型提供不同的适配器
总结
一个DDD重构的实践过程
战略设计可以独立于战术设计进行,战略设计有利于重构业务模型,梳理明确业务才能保证开发的顺利进行,即使不落地战术设计,对系统开发也有着重要的指导意义。
扩展
现实中业务域要和系统中的模型要做到一致,系统要能虚拟出现实中的各种关系,这样才能做到系统优雅自然地随着外部业务需求的变化而变化。所以,架构一个信息化的系统,首先要了解这个系统应用的外部组织结构是如何工作的
但现实中的业务模型可能也是混乱的,这就需要对现实中的业务模型再进行抽象和优化。
子域的拆分要根据业务需求以及可能发展的方向来,这个可能只有产品经理是最了解的,工程师如果能通过领域模型和产品经理确认需求,可能会更好一点。
子域设计好以后,所有的需求都是在子域内实现,通过调整子域内以及子域间的交互来实现需求,这样的思路下,一个需求的实现逻辑其实就是固定的,不会再出现有多种实现方式。
一个系统的发展,相当于维护领域的内在一致性,当出现业务发展和领域模型不一致时,就需要及时调整领域,这有可能是前期对领域的设计有问题,也有可能是当前的领域已经不能适应业务的发展了。
资深程序员真正的优势是他在一个业务领域的多年沉淀,对业务领域有更深刻的理解和认知,通过实践DDD可以将这些沉淀反映到工作中,反映到代码中。这也是一种架构。