架构设计原则与规约
2021-11-24 15:22:11 0 举报
AI智能生成
架构设计原则与规约
作者其他创作
大纲/内容
微服务的基本设计原则
设计原则值分层架构
微服务应用的内部分层的三层结构
老掉牙的MVC
Model
Service、Dao
View
Page
Controller
Controller
这个有种食之无味,弃之可惜感觉
登录(已废弃)
Login
Error
封装返回对象
简而言之,MVC滚犊子,MVC老掉牙了
微软推荐架构
数据访问层
业务逻辑层
UI层
阿里系模块封层
弱化Controller作用
Service加强
视图无关!
关注服务本身
JSON
三个主要的Module
API
RPC核心就是API,Dubbo举例
Service
没有Controller
dao/repo
设计原则之服务拆分
不同维度
压力模型
高频高并发流量
商品详情页
优惠计算
低频突发流量
秒杀
批量上架
场景处理方式
服务隔离
高频低频隔离
减少服务雪崩
保护其他服务
保障流控措施
隔离热点
热点数据
底层数据隔离
热点服务
主链路规划核心功能
主链路
搜索
详情
Cart
下单
支付
隐藏链路-营销优惠计算
搜索
详情
下单
技术解决办法
服务隔离
避免边缘业务影响
异常容错
流控
弹性计算
弹性伸缩能力
领域模型拆分DDD
服务拆分与合并
用户群体拆分
toC
toB
运营
采购
相同领域、独有场景
前后台业务
商家前台的下单链路
商家后台
运营后台
领域设计模型,适合业务的就是最好的,没有最好的
新零售业务的微服务拆分
背景
时间紧
快速验证业务模式
任务重
一月放一天假
责任大
集团的希望,五星目标
上面三大特点:形成糙快猛的文化
多元化入口
终端用户-卖家
App终端
手淘导流
线下POS
运营
Web业务系统
其他内部工作人员
履约终端APP
拣货终端
扫码枪
业务需求
商品模块
核心业务
订单中心
履约生命周期
订单生命周期
履约中心
库存模块
用户中心
搜索模块
后台运营系统
营销模块
导购投放系统
搜索推荐
广告展示
购物车模块
一个模块等于多个模块
商品中心
单体应用
拥有功能
商品发布
商品上下架
详情页服务
商品搜索
图文编辑
服务混战,改造成为必然趋势
老城区改造计划
抗压维度
低频瞬时流量场景
商品发布服务
库存发布
高频匀速流量
商品详情页
商品搜索服务
热点数据
服务隔离
数据隔离
业务维度
定时任务
改价单
库存发布计划
主链路+用户场景
商品详情页
商品搜索服务
广告投放
营销优惠计算领优惠券
购物车订单叶商品信息
运营场景
商品编辑
预售商品设置
营销规则设置
商品类目编辑
商品发布服务
优惠券模板
指导方针
隔离业务场景
剥离高频接口
新城区全貌图
淘系中台
IC订单
淘系商品
UMP
支付系统
淘系支撑服务
中间件配置中心
详情页微服务
商品发布微服务
RocketMQ
服务淘宝微服务
商品营销微服务
商品导购微服务
商品资源位微服务
商品后台批量任务
改价单和库存发布
改价单和库存发布
商品搜索微服务
OpenSearch
运营微服务
非主链路微服务
设计原则之版本控制
背景
兼容老版本客户端请求
APP1.2
单商品下单
APP1.3
店内但商品下单
APP1.4
跨店下单
同事能够运行多个版本服务
实现方案
Http Api 如何实现版本管理
uri设置版本
header指定版本
RPC Api 版本管理
RPC协议参数
版本判断逻辑
if/else判断逻辑
设计原则之无状态服务
什么是有状态
有状态服务-上下文依赖
来个BUFF,单机session
Hash+本地缓存的热点方案
水平扩展的局限性
是因为单机的上下文依赖造成的
分发、处理、存储的三个阶段
应用无状态、配置管理有状态
可用性保障手段-流量整形和分布式限流
限流算法
令牌桶算法
访问请求,队列满则丢弃
可选择队列
限制获取令牌个数
处理突发流量
漏桶算法
放入桶里面的是请求
控制流出速度,匀速流出方式,控制突发流量
调整参数
突发流量
堆积能力
增强算法
预热模型
Guava单机版限流工具
分布式限流四象限
访问频率
IP连接数
黑白名单规则
传输速率
中心化方式限流
网关层限流
手淘mtop
Nginx+Lua
F5限流方式
业务层限流
Gateway/Zuul
降级
熔断
路由
限流组件
Sentinel
Redis+Lua
单机Guava
限流
504、503、502等可以选择降级
降级
前端处理
后端处理
设计原则之EDD事件驱动架构
EDD应用场景
发生打劫
警察
救护车
紧急疏散
一般做法
告知
疏散
EDA
异步处理
异步处理
跨平台/跨语言通信方式
应用解耦
可靠投递
保证消费,事务性消息,原子性消费
最终一致性
适可而止
经典案例
EDD驱动的实时账务系统设计
三段论拆解问题
Golden gate捕获Event时间
Kafka集群
分发消息
卖家支付
会计账目
报表系统
退款流程
三段论拆解问题
每一个架构问题都是以大化小的方式
以大化小
捕捉支付事件
业务提出方不配合
消费事件
职责领域划分
Event Bus
Consumer
会计账目
报表系统
退款流程
数据同步工具
数据源同步
设计原则之数据一致性
Base理论
BA-Basically Available
基本可用
翻译为凑合用
主链路
降级
弹性计算
响应时间
一致性
最终一致性
系统功能可用
S-Soft State
软状态
也可以范围以中间状态
副本1:数据库
副本2:缓存
E-Eventually Consistent
最终一致性
高并发必备!
没有第二选择,只有最终一致性
银行系统除外
老系统,采用强一致性
When & How
什么时间点
如何完成的
缓存一致性
一致个鬼
定期失效
定期刷新
热点主动构建
必须是要求不高的系统才行
数据一致性
Seata应用
At全局事务回滚
TCC补偿
可用性有限,在可用性基础上实现最终一致性
幂等性理论
相同的资源
跟调用次数无关
一次调用
多次调用
单次与补偿
业务结果相同
Why
最终一致性-补偿重试
异常访问
举例说明
INSERT
账号注册
业务唯一约束
创建订单
分布式锁 + 隐藏表单
UPDATE
个人信息修改
乐观锁控制
set version+1,where version=xx
并发修改
分布式锁+DB锁
select xx from XX for update
DELETE
业务唯一约束
SELECT
无需锁操作
问题1:微服务拆分维度
题眼
领域模型划分
场景维度(高低频)
资源维度(IO/宽带/计算)
问题2:突发流量方案
流量整形
限流
预热
问题3:微服务的一致性
BASE理论
分布式事务
分布式锁
幂等性保证
问题4:手机APP升级,接口如何提供兼容性
RPC接口版本管理
作业
领域模型和业务角度对系统进行微服务拆分
公司项目入手、市场上应用
脱离分布式事务,你能想到多少种一致性方案
分布式事务应用并不十分广泛应用(成本+复杂度)
研究公开技术文章,了解大厂微服务实践(拆分)
架构设计原则
基本原则
六大派-Solid
开闭原则
核心问题
对扩展开放
抽象
封装变化
对修改关闭
对于新业务,采用扩展方式满足,而不是修改
网课程例子
网课程接口问题设计
Course sell()
Course sell()
sellCourseA
sellCourseB
sellCourseC
当sellCourseD到来的时候,需要修改代码,因此这个设计不合理
网课程接口优化
Course sell()
Course sell()
getPrice
sellCourse
设置底层扩展
NormalCourse
ArticlCourse
FreeCourse
Spring有很多的扩展点可以研究一下
微观抽象原理
扩展开发开放
重构
单一职责原则
网课案例
按照上面的设计
Course 设计
Course getDetail()
Double calPrice()
Long buy()
Connection getCon()
上面无法满足单一原则,一个Course干了所有的事情
单一职责的优化设计
Course只有负责course
Course getDetail(Long)
PriceEngine负责价格
Double calPrice(Long)
OrderCenter底层支付
Long placeOrder()
迪米特原则
迪米特原则(Law of Demeter)是指一个对象应该尽可能的保持高冷的姿态,少和其他对象勾搭,尽量对自己以外的对象保持最小了解。
因此,迪米特原则又叫做最少知道原则(Least KnowledgePrinciple)。一句话总结就是 - 不要和陌生人说话。
因此,迪米特原则又叫做最少知道原则(Least KnowledgePrinciple)。一句话总结就是 - 不要和陌生人说话。
在软件的微观架构层面
通过迪米特法则我们可以尽可能降低类之间的耦合。
对于一个类来说,什么是朋友,什么是陌生人呢?
通过学校小姐姐,选择最风骚的老师
小姐姐通过筛选方法选择风骚的老师
老师跟学生之间是陌生人,小姐姐跟学生是朋友
只需要学生跟小姐姐打交道就成了,不需要跟老师关联,就能得到想要的结果
直接关联的是朋友,间接使用或者目标的是陌生人
里式替换原则
里氏替换原则(Liskov Substitution Principle)的原文需要汉语十级才能读的明白。
咱们简单来说就是对于继承关系的父子类来说,子类可以对父类的功能进行扩展,但不能改变父类中已经定义的功能。
咱们简单来说就是对于继承关系的父子类来说,子类可以对父类的功能进行扩展,但不能改变父类中已经定义的功能。
子类可以添加自己独有的方法(废话,不然定义子类干什么)
子类重载父类方法时,方法的入参要比父类的入参更为宽松(接收更抽象的类),但返回值要比父类更严格(返回更具体的类)
子类必须实现父类抽象方法,但不能覆盖父类中非抽象的正常业务逻辑
里式替换其实是为了我们的继承结构着想,用来约法三章来约束继承的泛滥。
public abstract class Teacher {
public void saySomthing(){
System.out.println("我们的口号是" + “同学们好” );
}
}
// 正常老师
public class NormalTeacher extends Teacher {
public void saySomthing(){
System.out.println("我们的口号是" + “同学们好” );
}
}
// 不正常老师
public class FengSaoTeacher extends Teacher {
@Override
public void saySomthing(){
System.out.println("Hello同学们大家好" );
}
}
以上结构在实现功能上没有问题,但是违反了里式替换原则里的一点 - 子类必须实现父类抽象方法,但不能覆盖父类中非抽象的正常业务逻辑。
public void saySomthing(){
System.out.println("我们的口号是" + “同学们好” );
}
}
// 正常老师
public class NormalTeacher extends Teacher {
public void saySomthing(){
System.out.println("我们的口号是" + “同学们好” );
}
}
// 不正常老师
public class FengSaoTeacher extends Teacher {
@Override
public void saySomthing(){
System.out.println("Hello同学们大家好" );
}
}
以上结构在实现功能上没有问题,但是违反了里式替换原则里的一点 - 子类必须实现父类抽象方法,但不能覆盖父类中非抽象的正常业务逻辑。
public abstract class Teacher {
public void saySomthing(){
System.out.println("我们的口号是" + say() );
}
}
public class NormalTeacher extends Teacher {
@Override
public abstract String say() {
return "同学们好";
}
}
public class FengSaoTeacher extends Teacher {
@Override
public String say() {
return "同学们大家好";
}
}
上面的例子当中四户看出来前面的影子,开闭原则,对扩展开放,对修改关闭,对父类行为的扩展,不仅仅可以通过声明一个新的类来做,也可以在方法级别声明这样一个扩展点。所以在架构领域,很多原则和准则都是相通的。
public void saySomthing(){
System.out.println("我们的口号是" + say() );
}
}
public class NormalTeacher extends Teacher {
@Override
public abstract String say() {
return "同学们好";
}
}
public class FengSaoTeacher extends Teacher {
@Override
public String say() {
return "同学们大家好";
}
}
上面的例子当中四户看出来前面的影子,开闭原则,对扩展开放,对修改关闭,对父类行为的扩展,不仅仅可以通过声明一个新的类来做,也可以在方法级别声明这样一个扩展点。所以在架构领域,很多原则和准则都是相通的。
组合/聚合复用原则
组合优于继承
集成--->树状结构--->封闭性(父类变化影响子类,打破开闭原则)
组合原则经典应用
树状结构
组合声明接口
Componet接口声明
add()
remove()
myService()
leaf继承子类
myService()
组合关系Composit子类
add()
remove()
myService()
聚合运用
迭代器
java.util.Iterator容器类
Builder
PreparedStatement
Decorator
Stream输入输出流
Strategy
Comparable & Comparator
依赖倒置原则
从出道至今就被各路面试官问“讲一讲Spring的依赖倒置”,那同学们真的理解什么是依赖倒置吗?
所谓依赖,从程序的角度看,就是比如A要调用B的方法,那么A就依赖于B,反正A要用到B,则A依赖于B。
所谓倒置,你必须理解如果不倒置,会怎么着,因为A必须要有B,才可以调用B,如果不倒置,意思就是A主动获取B的实例:B b = new B(),这就是最简单的获取B实例的方法(当然还有各种设计模式可以帮助你去获得B的实例,比如工厂、Locator等等),然后你就可以调用b对象了。
依赖倒置原则(Dependence Inversion Principle)是程序要依赖于抽象接口,不要依赖于具体实现(面向接口编程)。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
通过面向接口编程使程序中模块的依赖程度降低,达到低耦合的功能,重用性加强。
依赖倒置原则
上层模块不应该依赖底层模块,它们都应该依赖于抽象(模块之间需要通过一个介质(抽象,接口)来实现关联)。
抽象不应该依赖于细节,具体细节依赖抽象。
如何将依赖倒置的理念应用在微观代码架构层面呢?
两个核心甩锅思想
上层模块不依赖底层模块的实现,而应该对底层模块做抽象。
抽象不应该依赖于底层细节,而细节应该依赖于抽象。
经典案例
通过面向接口编程使程序中模块的依赖程度降低,达到低耦合的功能,重用性加强。
电脑屏幕作为最“上层”的模块,在它之下有主板,主板再往下有CPU、显卡、内存条。
那么按照我们的常识来说,处于上层的显示器需要依赖于下层模块“主板”吗?
如果主板换了,难道显示器也要跟随着一并换掉吗?当然不需要
如果主板换了,难道显示器也要跟随着一并换掉吗?当然不需要
思考这样一个问题,应该怎么做才能让显示器、主板、显卡内存这一条上下游链路玩得转,使上层模块脱离底层模块的束缚?
这就是第二个“抽象”原则里讲到的方法,由上层模块定义抽象接口,下游模块依赖于这个抽象来做适配。
我们这样思考,显示器定义了一套串行外设接口(抽象层),而主板(底层细节)依赖于这个抽象层来适配上层应用。
这样即便更换了主板,我们也并不需要对显示器做任何变更。
这样即便更换了主板,我们也并不需要对显示器做任何变更。
软件设计层面
层模块通过定义抽象接口对底层组件的访问进行约定,底层组件依赖于抽象接口实现细节,这个设计理念在JDK和Spring里随处可见。
案例说明
架构师相亲
假设这是一个蚂蚁金服的架构师,借着蚂蚁上市身价已经达到了一个小目标,各种各样的女嘉宾都来求包养。
架构师并不迁就底层对象,他定义了一个抽象层叫IGirl
架构师并不迁就底层对象,他定义了一个抽象层叫IGirl
来了两位女嘉宾,分别是御姐和萝莉,她们都依赖于抽象接口实现各自特殊的对话,对于架构师来说并不需要关注底层的女嘉宾是什么来历,统统来者不拒全盘接收,底层的改变并不会影响到上层应用。
接口隔离原则
接口隔离原则(InterfaceSegregationPrinciple,ISP)是指用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接口。
核心要点
一个类对一类的依赖应该建立在最小的接口之上。
建立单一接口,不要建立庞大臃肿的接口。
尽量细化接口,接口中的方法尽量少(不是越少越好,一定要适度)。
建立方法
接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑;
为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法;
提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
高并发设计原则
局部并发原则
买买买的设计有哪些模块
获取商品信息
获取库存信息
计算优惠信息
获取地址信息
创建订单
生成订单快照
生成支付链接
自动/手动付款
释放/减少库存
买买买的设
准备无关联信息查询
获取商品信息
获取地址信息
账户信息检查
数据检查点1
生成支付链接
计算优惠信息
库存锁定
数据检查点2
生成订单快照
创建订单
释放/减少库存
拆分与服务化
服务化拆分的维度
业务维度
商品服务
订单服务
库存服务
营销服务
功能维度
全模板
领券服务
核销服务
资源维度
高频场景
比如:商品搜索
低频场景
IO密集
计算密集
目的是为了主链路规划
高可用设计原则
降级
主动降级
开关推送
配置中心
请访问其他页面
等等
被动降级
超时降级
异常降级
失败率
熔断保护
经典案例
多级降级
Redis的local静默
核心主链路出现问题如何处理
限流
网管层限流
Nginx
Limit模块
Gateway
Redis+Lua
业务层限流
本地限流
Guava,单机限流轻量级别的
分布式限流
Sentinel,分布式的限流比较重
限流如何设置的?
通过压测确定的,统一规划的
恶意攻击
恶意IP
Nginx deny解决
DDos攻击
托管平台防DDos的云平台来确定
访问量RT
动态限流
弹性计算
主链路弹性
主搜-->详情-->购物车-->下单
支路弹性
直通车排名-->主搜<--全文检索--优惠透出
用户画像推荐-->详情<--用户评论、优惠信息、客服通道
购物车<--收藏夹、底部推荐
下单<--没有了
弹性伸缩-k8s+docker,主链路要弹都弹
流量切换
多机房环境
DNS端
域名切换入口,Client端流量调度(推送)
虚拟IP
HaProxy LVS负载均衡
应用层
Nginx+服务网管
大型应用中必不可少的
阿里如何保证流量切换的
三机房多活
版本控制和回滚策略
版本控制
配置中心
CICD Pipeline
万物皆可回滚
系统设计思想
保持简单轻量的架构
Try everything you can to Keep it simple
Try everything you can to Keep it simple
不要重复你自己
Don't repeat yourself-DRY
减少信息重复度
不要造轮子
别人已经做了,就不要去再做个
不劳而获
学会不劳而获,尽可能少写代码
如何处理
代码复用
工具包
Lib
工具类
Guava
lombok
长Code是魔鬼
重构+设计模式
保持它简单与傻瓜
Keep is simple and stupid - KISS
学车例子
手动挡
自动挡
框架层面
无感知
无侵入
接入成本
0/极少配置
Clean Code
总之实现一句话描述
你不需要它
You aren't gnna need it - YAGNI
避免过度设计
分布式事务
数据异构
规则引擎
Graph QL
上面就是脱裤子放屁
组件间交互和行为
命令查询分离
Command Query Separation - CQS
曾经只是职责分离
读写分离
读多写少,读库横向扩展
超高并发场景保证写入
数据异构和数据聚合
ES之类
允许短时间内的不一致
最终一致性
关注点分离
Separation of Concerns -SOC
横向业务
服务拆分
模块拆分
纵向业务
MVC
Manager
DAO
Repositoty
无感知
切面(AOP)--事务,权限
代理--阿里的Seata
幕后--Spring Boot的Starter、Lib
约定大于配置
架构层面的发展趋势
决定我们的技术选型的方向
约定大于配置
Convention over Configuration - COC
定制化,默认值
隐藏个性,突出共性
代码量少
框架层面
案例说明
Spring Boot
环境变量:application-xxx.yml
多数据源
Starter
技术选型
Spring Data
SQL关键字
属性
目标实现拆箱即用
题目1:你的项目整体架构规划
全局视角
核心模块架构图(运用了哪些原则)
局部视角
你所负责的模块内部分层(架构原则)
题目2:高并发业务场景有哪些思考方向
大方向前提
微服务+高可用原则
应用层面
限流-降级熔断-削峰填谷-分表分库+数据异构-分段缓存-局部并发
细节
用场景局里读写分析(秒杀、点赞、关注)
题目3:水货架构师爱问的问题-谈谈对Spring的理解
跑掉AOP和IOC这种小学生都会的问题
设计理念-插件式(具体举例),约定大于配置等理念层面的具体落地组件
任务:
公司业务中高并发(相对)场景分析
我司platform、infra团队封装的蹩脚工具
研究各种技术文章,了解高并发业务的解决方案
红包
春晚红包
全局发号器
订单号生成
点对点消息ID
秒杀
抢单
0 条评论
下一页