第十周作业总结
一、背景:
1、单体应用的问题:
1. 形成巨无霸应用,编译打包困难,打包出来的包有四五个 G。
2. 代码分支管理困难。
3. 单体应用部署大规模集群,导致数据库连接不够用。
4. 新增业务困难。
2、解决方案:
1. 拆分,将模块独立出来开发部署,降低系统的耦合性。
1) 纵向拆分,将一个大应用拆分为多个小应用,如果新增业务比较独立,那么直接将其设计部署为一个独立的应用。
2) 横向拆分,将复用的业务拆分出来,独立部署为微服务,新增业务只需要调用这些微服务。
3、难点:
微服务的业务和服务的划分,以及服务的开发和维护上。所以拆分之前要对业务系统很了解,堆拆分的每一个微服务定义好职责边界。
二、Web Service 与企业级分布式服务
1、缺点:
1. 臃肿的注册与发现机制
2. 低效的 XML 序列化手段
3. 开销较高的 HTTP 通信
4. 复杂的部署与维护
三、微服务框架
1、基本需求:
1. 负载均衡
2. 失效转移(Fail Over):
调用的服务是不可用的,要及时转移,切换到其他服务上。
3. 高效的远程通信:
在微服务中,服务之间的调用是很频繁的,核心服务每天的调用次数回达到数以亿计,如果没有高效的通信手段。服务调用可能会成为系统性能的瓶颈。(应使用基于长连接的通信,序列化的应简单,减少数据传输)
4. 对应用最少侵入:
是否使用微服务需要根据业务的发展规划,微服务也需要循序渐进式的演化,甚至会出现使用了微服务后,退回到集中式部署。
5. 版本管理:
为了响应快速变化的需求,服务升级不可避免。如果访问的接口发生了变化,就需要服务请求者和服务提供者同时升级,而同时升级会导致网站服务不可用。因此要服务提供者先升级接口,同时提供旧接口的调用,当请求者访问接口升级了之后才能关闭旧接口。
四、微服务架构落地
1、业务先行,先理顺业务边界和依赖,技术是手段而不是目的。
2、先有独立的模块,后有分布式的服务。
3、业务耦合严重,逻辑复杂多变的系统进行微服务重构要谨慎。
4、要搞清除微服务的目的是什么,业务复用?开发边界清晰?分布式集群提升性能?
5、最佳实践:
1. 命令与查询职责隔离:
1) 含义:在服务接口层面,将查询(读操作)与命令(写操作)隔离,实现服务层的读写分离。
2) 优点:能针对读写分别优化,查询服务不会修改数据,更好的保护数据。
3) 实现:设计一个专门的读微服务和写微服务,都访问不同的数据库。独立部署。网关别分调用。读微服务可以更多的使用缓存提升读性能,写微服务可以通过消息队列,异步提升写性能。
2. 事件溯源:
1) 含义:将用户请求处理过程中的每次状态变化都记录到事件日志中,并按照时间序列经行持久化存储。
2) 优点:可以复现用户任何状态,进行复核审计。可以有效地监控用户状态变化,并在此基础上实现分布式事务。
3) 实现:
3. 断路器:
1) 含义:当某个服务出现故障,响应延迟或者失败率增加,继续调用这个服务会导致调用者请求阻塞,资源耗尽,进而出现服务级联失效。断路器三种状态:关闭、打开、半开。
4. 服务重试及调用超时:
上游调用者超时时间要大于下游调用者超时时间之和。
5. 最重要的是需求。
五、微服务网关的技术架构
1、背景:
对于微服务的调用,不再是通过对一些复杂的 WEB 应用的直接调用,而是通过一个网关调用。
APP,WEB 等调用,会先调用网关,然后网关调用不同的微服务。
2、网关作用:
1. 统一接入:高性能、高并发、高可靠、负载均衡
2. 安全防护:防刷控制、黑白名单
3. 流量管控与容错:限流、降级、熔断
4. 协议适配:HTPP、Dubbo、JSF(不同的微服务协议,一个网关适配多个微服务协议)
3、网管管道技术:
网关本身没有什么业务,主要职责是做各种校验与拦截,这些职责可以通过管道技术连接起来。
--request-->参数校验--->限流控制--->接口调用--request-->
4、异步网关与异步微服务框架
利用 Servlet3 实现异步网关。req.startAsync()返回一个 AsyncContext 就可以结束当前线程了,request 对象和 response 对象都在 context 里面,整个 socket 都还在保持着。要返回 response 结果的时候,从 AsyncContext 里面获得一个 response 输出。
传统的同步 WEB 编程,如果线程释放了,socket 连接也就会释放了。Servlet3 的异步网络编程,可以释放掉当前线程,但是不会释放掉 socket 连接,可以通过 AysncContext 获得之前的连接。
5、开放平台网关:
1. 开放平台网关暴露一些 API 给第三方合作者使用。要支持:协议转换、安全、审计、路由、流程。
2. 开放授权协议 OAuth2.0:
步骤:
1) 第三方应用向资源所有者(用户)请求授权
2) 资源所有者(用户),同意授权
3) 第三方应用向授权服务器申请令牌
4) 授权服务器发放令牌
5) 第三方应用使用令牌向资源服务器访问资源
6) 资源服务器确认令牌,发放资源。
六、领域驱动设计 DDD
1、背景:
1.事务脚本(贫血模型):
Controller -> Service -> Dao,导致重 Service,Service 会越来越臃肿。
2.领域模型(充血模型):
根据对象,每个对象承担自己的业务职责和业务逻辑。通过对象的组合方式完成业务功能。(把复杂的业务逻辑拆分成不同的对象去完成)
3. 贫血模型:
Service、Dao 这些对象只有方法,没有数值成员变量,而方法调用时传递的数值对象,因此事务脚本又被称作贫血模型。
4. 充血模型:
领域模型是合并了行为和数据的领域的对象模型。
通过领域模型对象的交互完成业务逻辑的实现,也就是说,设计好了领域模型对象,也就设计好了业务逻辑实现。和事务脚本被称作贫血模型相对应的,领域模型也被称为充血模型。
2、DDD 设计:
1. 战略设计:
高层的,业务架构层面。
2. 战术设计:
底层的,编程编码方面的实现。
3、战略设计:
(1) 领域:
1. 领域是一个组织所做的事情以及其包含的一切,通俗地说,就是组织的业务范围和做事方式,也是软件开发的目标范围。
2. 领域驱动设计就是从业务领域出发,分析领域内模型及其关系,进而设计软件系统的方法。
3. 将大领域拆分成子领域。
(2) 限界上下文:
1. 在一个子域中,会创建一个概念上的领域边界,在这个边界中,任何领域对象都只表示特定于该边界内部的确切含义。这样边界便称为限界上下文。限界上下文和子域具有一对一的关系,用来控制子域的边界,保证子域内的概念统一性。
2. 通常限界上下文对应一个组件或者一个模块,或者一个微服务,一个子系统。
(3) 上下文映射图:
不同的限界上下文,也就是不同的子系统或者模块之间会有各种的交互合作。DDD 使用上下文映射图来设计这种关联和交互。
4、战术设计:
(1) 实体:
领域模型对象也被称为实体,每个实体都是唯一的,具有一个唯一标识。
实体设计是 DDD 的核心所在,首先通过业务分析,识别出实体对象,然后通过相关的业务逻辑设计实体的属性和方法。
(2) 值对象:
并不是领域内的对象都应该被设计为实体,DDD 推荐尽可能将对象设计为值对象。
比如像住址这样的对象就是典型的值对象,也许建在住址上的房子可以被当做一个实体,但是住址仅仅是对房子的一个描述,像这样仅仅用来做度量或描述的对象应该被设计为值对象。
值对象的一个特点是不变性,一个值对象创建以后就不能再改变了。
(3) 聚合:
聚合是一个关联对象的集合,我们将其作为一个单元来处理数据更改。每个集合都有一个根和一个边界。边界定义了聚合内部的内容。根是聚合中包含的单个特定实体。
聚合根:将多个实体和值对象聚合在一起的实体。
5、DDD 分层架构:
(1) 用户接口层:
和贫血模型的 Controller 层一样。
(2) 应用层:
参数处理,事务控制,组合调用领域层。
(3) 领域层:
具体的业务操作。
6、DDD 六边形架构:
领域模型通过应用程序封装成一个相对比较独立的模块,而不同的外部系统则通过不同的适配器和领域模型交互,比如可以通过 HTTP 接口访问领域模型,也可以通过 Web Service 或者消息队列访问领域模型,只需要为这些不同的访问接口提供不同的适配器就可以了。
七、软件组件设计原则:
1、组件内聚原则:
1. 复用发布等同原则:
1) 软件复用的最小粒度应该等同于其发布的最小粒度。也就是说,如果你希望别人以怎样的粒度复用你的软件,你就应该以怎样的粒度发布你的软件。
2) 版本号约定建议:
a. 版本号格式:主版本号.次版本号.修订号。比如 1.3.12,在这个版本号中,主版本号是 1,次版本号是 3,修订号是 12。
b. 主版本号升级,表示组件发生了不向前兼容的重大修订;
c. 次版本号升级,表示组件进行了重要的功能修订或者 bug 修复,但是组件是向前兼容的;
d. 修订号升级,表示组件进行了不重要的功能修订或者 bug 修复。
2. 共同封闭原则:
我们应该将那些会同时修改,并且为了相同目的而修改的类放到同一个组件中。而将不会同时修改,并且不会为了相同目的而修改的类放到不同的组件中。
3. 共同复用原则:
共同复用原则是说,不要强迫一个组件的用户依赖他们不需要的东西。
2、组件耦合原则:
1. 无循环依赖原则:
无循环依赖原则说,组件依赖关系中不应该出现环。如果组件 A 依赖组件 B,组件 B 依赖组件 C,组件 C 又依赖组件 A,就形成了循环依赖。
2. 稳定依赖原则:
稳定依赖原则说,组件依赖关系必须指向更稳定的方向。较少变更的组件是稳定的,也就是说,经常变更的组件是不稳定的。根据稳定依赖原则,不稳定的组件应该依赖稳定的组件,而不是反过来。
稳定依赖原则通俗地说就是,组件不应该依赖一个比自己还不稳定的组件。
3. 稳定抽象原则:
稳定抽象原则说,一个组件的抽象化程度应该与其稳定性程度一致。也就是说,一个稳定的组件应该是抽象的,而不稳定的组件应该是具体的。
评论