架构模式
- 企业应用架构模式
一般来说,企业应用指的是大型系统
企业应用的特点:
- 持久化数据
- 大量数据
- 高并发
- 大量数据展示操作页面
- 多个系统集成
- 业务逻辑复杂
分层架构
将系统按照职责拆分和组织,上层依赖于下层定义的各种服务,下层对上层隐藏下下层的细节,最核心的一点就是需要保证各层之间的差异足够清晰,边界足够明显
层次并不能封装所有东西,有时会带来级联修改,过多的层次也会影响性能
- OSI七层模型
- CS/BS架构
- 企业应用分层
- 表现层
- 领域层
- 数据源层
为何分层
关注点分离(降低复杂度)
- 每个层中的组件只会处理本层的逻辑,在扩展某层时,其他层是不受影响的,通过这种方式可以支撑系统在某层上快速扩展
隔离变化/错误
- 提炼变化层与稳定层隔离变化
stateDiagram-v2
state 数据接入层 {
外部预警1 --> [*]
外部预警2 --> [*]
外部预警3 --> [*]
}
数据接入层 --> 落库业务层
- 提炼抽象层与实现层
classDiagram
class 地址查询策略
地址查询策略: +查询(手机号)
class 外部接口查询策略1
外部接口查询策略1: +查询(手机号)
class 外部接口查询策略2
外部接口查询策略2: +查询(手机号)
class 外部接口查询策略3
外部接口查询策略3: +查询(手机号)
地址查询策略 <|-- 外部接口查询策略1
地址查询策略 <|-- 外部接口查询策略2
地址查询策略 <|-- 外部接口查询策略3
扩展性伸缩性
可测试性
分层的问题
- 性能开销:通信、数据转化
- 开发成本
分层架构设计
- 依赖原则
- 定义职责:职责分离 高层抽象
- 层技术栈选择:每层需求自定 参考成功架构 适合自己组织
- 集成:单层内部的单元测试 -> 层之间的集成测试
分层架构模型
MVC
随着前后端分离以及后端微服务化的不断发展,传统的MVC已不适合现在的纯后端系统,Controller其实也是API,它代表了服务对外提供的接口,是一种合约
- model:模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器
- view:视图代表模型包含的数据的可视化
- controller:控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开
优势
- 清晰的职责划分
- 组件独立,代码重用
- 后期维护方便
- 适合任何项目
弊端
- 展示数据慢(针对jsp)
- 对开发者架构设计能力要求高
- 异步交互不方便
Model
业务数据模型,Model层是高层策略,是技术无关逻辑的封装
View
当有用户的行为触发操作时,由控制器更新模型,并通知视图进行更新
Controller
主要是接收用户请求,并负责协调Model与View
MVP
- Presenter较为复杂 可以脱离view进行测试
MVVM
BFF
- 为前端而生的后端服务
事件驱动架构
- 异步分发事件
- 发生的事件
- 变化的状态
- 耦合较低 灵活 扩展较容易
- 由于异步与细粒度的处理单元 正常情况下性能较高 但在出现异常重试时性能会降低
- 集成测试、维护很难
两种模式
Mediator
- Event Queue:只关注事件的接收和发送
- Event Mediator:将原始事件转化成业务事件
- Event Channel:业务事件的聚合 由感兴趣的processor监听
- Event Processor:业务事件执行单元
Broker
- 无中心编排控制点
- 无业务逻辑的消息分发
两种模式的processor都是单一职责的最小执行单元
微内核架构
- 也被称为插件化架构,是一种面向功能进行拆分的可扩展的架构
系统核心的作用在于资源封装与插件规范定义
插件为在核心提供的接口上实现其单一的功能 插件之间应避免依赖 不能影响核心
优点
- 符合开闭原则 核心稳定 插件可扩展
- 内核与插件之间的解耦与隔离
- 针对核心的统一管理及插件的管理带来的性能优势
- 动态插件带来的部署上的优势
缺点
- 开发难度高
- 功能位于核心还是插件难以划分
- 热插拔
- 注册协议 通信协议
- 难以伸缩
设计关键点
- 插件管理:核心系统要知道当前哪些插件可用、如何加载这些插件、什么时候加载这些插件
- 插件连接:核心系统必须制定插件和核心系统的连接规范
- 插件通信:设计的插件间是完全解耦的,但是实际应用中,必然存在某个业务需要多个插件协作,这要求插件间进行通信
系统核心
核心的功能为MVP 所有核心能实现的接口都要经过核心
开放规范
- 版本兼容
- 上下文、环境参数规范
- 回调 钩子 事件
- 业务集成规范
注册规范
标识、功能、位置、依赖、权限
通信机制
- 同步异步
- 本地远程
- 数据格式
插件装载
- 知道插件在哪
- 何时 启动期 运行期
- 何地 本地 远程
生产者消费者模式
平衡问题:数据不堆积、不浪费消费者资源 调度算法
EDA关注的是事件触发、传递整体流程 生产者消费者关注的数据的传递与存储处理具体流程
优点
- 三者之间低耦合、异步
- 天生可以分布式
- 协调生产与消费速度不一致
- 生产数据与消费数据的分离
缺点
生产者
系统运转的动力来源 重点关注发送数据到容器 与消费者彻底解耦
- 发送确认
- 重试机制
- 同步异步
- 序列化
- 异常处理
消费者
获取数据-业务逻辑处理 要向容器确认数据被消费
- 消费方式 poll push
- 分发方式 queue topic
- 消费策略
- 消费幂等性
- 反序列化
- 多消费者
- 复制
- 非复制
容器
保管数据
- 存多久(时效性) 存哪里
- 如何调度数据给消费者
- 消费保证
- at least once
- at most once
- exactly once 成本很高
数据单元
- 传输的数据基本单位
具备业务含义、传输过程保证数据单元完整性、尽量保证单元之间无依赖
组织领域逻辑
- 编写业务逻辑
事务脚本
优点
- 易于理解
- 能与简单数据源层很好合作
- 事务边界容易划分
缺点
- 业务复杂导致代码冗余复杂
领域模型
- 使用面向对象的方法
开销来源于使用复杂以及数据源的复杂,还要面对将领域模型映射到数据库的问题
表模块
- 围绕表组织领域逻辑
服务层
- 将领域层再拆为两层,服务层提供简单的API接口
映射到关系数据库
架构模式
- 活动记录
- 类似于JAVA Bean
- 数据映射器
- ORM
行为
如何保证对领域对象的修改能及时存储到数据库
- 标志映射
- 保证相同的对象只被加载一次
- 延迟加载
读取数据
结构映射模式
关系的映射
- 外键映射一对多
- 关联表映射多对多
继承
classDiagram
class Animal {
+Id
+age
}
class Cat{
+weight
}
class Dog {
+height
}
Animal <|-- Cat
Animal <|-- Dog
- 单表继承
- 多个层次共享同一张表
CREATE TABLE Animal (
DB_TYPE
id,
age,
weight,
height
)
- 具体表继承
- 一个层次一张表
CREATE TABLE Animal (
id,
age
)
CREATE TABLE Cat (
id,
weight
)
CREATE TABLE Dog (
id,
height
)
- 类表继承
- 一个类一张表
CREATE TABLE Animal (
id,
age
)
CREATE TABLE Cat (
id,
age
weight
)
CREATE TABLE Dog (
id,
age
height
)
建立映射
将数据库设计看做一种持久化对象数据的方法
- 双向映射
- 将对象-数据库以及数据库-对象两种映射分开设计
元数据
- 通过元数据的描述自动生成代码
数据库连接
- 使用连接池管理连接
- 将连接与事务绑定在一起
web表现层
模板视图
jsp php
转换视图
json
两阶视图
生成一个逻辑视图,再将逻辑视图对应到html
并发
本质问题
- 更新丢失
- 不一致读
执行语境
- 一个请求对应一个会话,可以使进程,也可以是线程,但创建进程耗费资源,使用线程又会导致线程安全问题
- 数据库中的语境是事务
方案
- 隔离:划分数据,一片数据只能被一个工作单元访问
- 不变:不变的数据是线程安全的
乐观锁与悲观锁
- 使用读写控制
- 死锁
事务
事务是一个又边界的工作序列,开始和结束都有明确定义
- 事务的特性
事务资源
横跨多个请求的被称为长事务
锁升级:一个事务锁住了许多行,则直接升级到对整个表的锁
减少事务隔离提高灵活性
可串行化:当并发执行的结果与以某种顺序一致时
系统事务与业务事务
离线并发控制
只有在数据提交失败的时候才能发现
应用服务器并发
使用每会话一个进程的方式来避免处理线程的麻烦
会话状态
- 无状态服务器
存储方法
- 客户端存储
- cookie
- 注意会话数据大小以及数据安全性完整性
- 服务器存储
- session
- 数据库存储
- 将会话信息存储在数据库中
会话迁移
会话可以在服务器集群之间转移
分布
远程接口与本地接口
进程内的过程调用非常快
何时必须使用分布对象
- 客户机与服务器之间
- 服务器与数据库之间
- web系统之间
- 使用软件包
分布边界
注意远程调用的边界
分布接口
- 基于XML与HTTP
如果系统基于相同的平台构建,最好使用系统自己的远程调用机制