架构设计
2023-05-09 15:02:05 2 举报
AI智能生成
架构设计
作者其他创作
大纲/内容
路由分发
拥有相应的应用管理机制
不需要 SEO 支持
iFarme
实现
微前端
前端架构
由高效的召回规则、算法或简单的模型组成,这让推荐系统能快速从海量的候选集中召回用户可能感兴趣的物品
单策略召回
多路召回
基于Embedding召回
召回层
精排层,是利用排序模型对初筛的候选集进行精排序
排序层
推荐系统
Transformer架构
encoder-decoder架构
建模相关平台(AI 中台)
模型执行平台(模型引擎)
应用架构
人工智能架构
单体架构
垂直架构
SOA代表面向服务的架构,将应用程序按照不同的职责划分为不同的模块,不同的模块直接通过特定的协议和接口进行交互。这样将整个系统切分成很多单个组件服务来完成请求,当流量过大时通过水平扩展相应的组件来支撑,所有组件通过交互来满足整体的业务需求。服务化架构是一套松耦合的架构,服务的拆分原则是服务内部高内聚,服务之间低耦合。这个阶段一般使用Web Service或者Dubbo来服务治理。
SOA服务化架构
1.微服务架构强调业务系统需要彻底的组件化和服务化,一个组件就是一个产品,可以单独对外提供服务
2.微服务不再强调传统SOA架构里面比较重要的ESB企业服务总线
3.微服务强调每个微服务都有自己独立的运行空间,包括数据库资源。
4.微服务架构本身来源于互联网的思路,因此组件对外发布的服务强调HTTP Rest API的方法进行
5.微服务的切分粒度会更小。
SOA和微服务的区别
导致制造大量的于数据表对应的类,这些类一般不会有行为,而描述业务逻辑的职责就会到很多service类上
Active Record 设计模式
Saga
可以用与语言无关的方式处理常见的客户端连接任务,如监视,日志记录,路由和安全性(如 TLS)。
外交官模式(Ambassador)
介于新应用和遗留应用之间,用于确保新应用的设计不受遗留应用的限制。
不共享相同语义的不同子系统之间实施外观或适配器层。 此层转换一个子系统向另一个子系统发出的请求。 使用反腐层(Anti-corruption layer)模式可确保应用程序的设计不受限于对外部子系统的依赖
反腐层(Anti-corruption layer)模式最先由 Eric Evans 在 Domain-Driven Design(域驱动的设计)中描述。
反腐层(Anti-corruption layer)
为不同类型的客户端(如桌面和移动设备)创建单独的后端服务。
BFF 后端服务前端(Backends for Frontends)
隔离了每个工作负载或服务的关键资源,如连接池、内存和 CPU。
轮船底下船舱一个个都是互相隔离的,这样一旦哪个舱壁出现漏水也影响不到其他的船舱
Hystrix线程池舱壁模式
舱壁模式(Bulkhead)
聚合服务 与 BFF有些类似
将对多个单独微服务的请求聚合成单个请求
网关聚合(Gateway Aggregation)
网关卸载(Gateway Offloading)
网关路由(Gateway Routing)
例:给原有php服务 封装一个边车 注册在java注册中心
将应用程序的辅助组件部署为单独的容器或进程以提供隔离和封装。
边车模式(Sidecar)
通过逐步重构单体应用(而不是推到重来的方式),逐渐构建出一个新的应用程序。
绞杀者模式(Strangler)
敏捷性
小型专属团队
小型代码库
混合技术
错误隔离
可伸缩性
数据隔离
优点
复杂性
开发和测试
挑战
理论
微服务
Spring Cloud或者Dubbo的成熟框架,直接搞定服务注册,服务发现,负载均衡,熔断等基础功能。然后自己开发服务路由等高级功能, 接入Zipkin等Apm做全链路监控,自己做加密、认证、授权。 想办法搞定灰度方案,用Redis等实现限速、配额。 诸如此类,一大堆的事情, 都需要自己做
服务治理周期长:微服务治理框架与业务耦合,上线周期长,策略调整周期长。
产品能力弱:Spring Cloud缺乏平台化和产品化的能力,可视化能力弱。
进化
Istio 的出现为负责的微服务架构减轻了很多的负担,开发者不用关心服务调用的超时、重试、Rate Limit 的实现,服务之间的安全、授权也自动得到了保证。
为 Envoy 提供了服务发现,流量管理和智能路由(AB 测试、金丝雀发布等),以及错误处理(超时、重试、熔断)功能。 用户通过 Pilot 的 API 管理网络相关的资源对象,Pilot 会根据用户的配置和服务的信息把网络流量管理变成 Envoy 能识别的格式分发到各个 Sidecar 代理中。
Pilot
为整个集群执行访问控制(哪些用户可以访问哪些服务)和 Policy 管理(Rate Limit,Quota 等),并且收集代理观察到的服务之间的流量统计数据。
Mixer
为服务之间提供认证和证书管理,可以让服务自动升级成 TLS 协议。
citadel
Envoy
概念
L4-6
VirtualService
Ingress gateway
L7
k8s Ingress
Ingress
Istio(服务网格)
应用架构发展
沟通成本 = N(N-1)/ 2,N代表沟通的总人数沟通的问题会影响系统设计,软件架构最终会是沟通(组织)结构的映射。
第一定律,组织沟通方式会通过系统设计表达出来。
时间永远不够,人力永远不足,事情永远做不完,一件一件慢慢来来。系统经过再严格的测试,总是会有问题。完美测试不存在,测试无法100%覆盖并保证系统没有问题,系统需要测试但永远避免不了问题。
第二定律,时间再多一件事情也不可能做的完美,但总有时间做完一件事情。
什么样的系统对应什么样的组织,什么样的组织设计出什么样的系统。架构由组织关系决定,架构服务于技术,同样服务于组织中的人
第三定律,线性系统和线性组织架构间有潜在的异质同态特性。
系统越复杂,越需越多的人手,需要越多的沟通,需要更高的成本。分而治之,以结构化、模块化的方式架构和设计系统,以小团队形式进行开发和沟通。
第四定律,大的系统组织总是比小系统更倾向于分解。
康威定律
多进程或者线程去进行处理,但是这样会带来一些进程切换的开销,试想一个进程一个数据读了500ms,期间进程切换到它3次,但是CPU却什么都不能干,就这么切换走了
CPU的处理速度是要远远快于IO速度的,如果CPU为了IO操作(例如从Socket读取一段数据)而阻塞显然是不划算的
回调的方式
事件驱动
操作系统
事件驱动架构是一种松耦合、分布式的驱动架构,收集到某应用产生的事件后实时对事件采取必要的处理后路由至下游系统,无需等待系统响应。
降低耦合:降低事件生产者和订阅者的耦合性。事件生产者只需关注事件的发生,无需关注事件如何处理以及被分发给哪些订阅者;任何一个环节出现故障,都不会影响其他业务正常运行;
异步执行:事件驱动架构适用于异步场景,即便是需求高峰期,收集各种来源的事件后保留在事件总线中,然后逐步分发传递事件,不会造成系统拥塞或资源过剩的情况;
可扩展性:事件驱动架构中路由和过滤能力支持划分服务,便于扩展和路由分发;
事件驱动架构(EDA)
如何增量地构建软件和如何部署软件
对部署而言,增量变更指业务功能的模块化和解耦水平,以及它们是如何映射到架构中去的。
对于开发者来说,变更范围相对更小
增量变更
该函数时一个目标函数,用于计算潜在的解决方案与既定目标的差距。在演化计算中,适应度函数决定一个算法是否在持续提升。
适应度函数驱动架构设计决策,并引导架构变更适应业务和技术环境的变化。
适应度函数
在高速变迁的业务、严谨的系统需求和架构特征间找到平衡
引导性变更
架构师确定了可审计性、数据、安全性、性能、合法性和伸缩性是该应用的关键架构特征。随着业务需求不断变化,每个架构特征都通过适应度函数来保护其完整性。
多个架构维度
演进式架构(Evolutionary Architecture)
为哪些用户提供什么服务
语境图
完成这个系统使用了哪些服务
容器图
每个服务是由哪些组件实现的
组件图
实现这个组件需要完成什么
类图
C4
理论技巧
充血
贫血
几种类型
问题域
应该围绕业务功能而不是数据访问或消息传递等水平层来设计微服务
定义系统的大规模结构
核心域
通用子域
支撑子域
领域类型
站在不同角度拆分不一致
销售子域
日志子域
除销售外子域:物流子域、用户子域、商品子域
eg.电商
营销、推荐、购物、直播
用户、订单、支付
数据字典、权限
领域拆分子域
什么内容应该在领域模型中实现,什么东西不应该出现
限界上下文本质上就是子域。当一个领域,我们划分的子域足够小的时候,就可以在子域内划分限界上下文,进行领域建模了,如果子域划分得足够小不可再拆分,那么限界上下文刚好和子域的边界一致,如果子域还能再次划分,一个子域内出现多个限界上下文,也是OK的
领域模型的边界和业务适用范围
销售子域中是商品
物流子域中是货品
eg.商品
限界上下文
有助于避免组织边界或技术选择左右你的设计
哪些功能密切相关
哪些功能是业务的核心
哪些功能提供辅助服务
什么是依赖项关系图?
在填充关系图时,可以开始标识离散的子域
构建业务域并创建 域模型
创建的系统的鸟瞰图
1.分析业务领域
2.定义领域的边界上下文
3.在边界上下文中,应用战术 DDD 模式以定义实体、聚合和域服务。
从域模型派生微服务
4.使用前一步骤的结果可以标识应用程序中的微服务
方案1
通用语言
由项目团队、领域专家等多人参与,采用头脑风暴的方式进行用户故事分析,找出并建立领域对象
事件风暴
核心业务单据
红色(时标型(Moment-Interval)对象)
绿色(PPT(Party/Place/Thing)对象参与方、地、物)
黄色(角色对象)
蓝色(描述对象)
收敛时标对象
划分领域
四色建模
画表格并写实例,管理核心领域“恰好够用”的数据,增强数据完整性,避免过度设计
限界纸笔建模
通过角色扮演模型使得领域模型易于理解
DCI建模
方案2
方案
每个服务承担单一责任
服务之间不存在琐碎的调用。
每个服务足够小,独立工作的小团队即可构建它
两个或更多个服务的部署不应该存在相互依赖的关系
服务未紧密耦合,可独立演变
服务边界不会造成数据一致性或完整性方面的问题
验证设计
战略
提供一组可用于创建域模型的设计模式
包括实体、聚合和领域服务。 借助这些战术模式,可以设计低耦合高内聚的微服务。
战术(策略)
两个阶段
服务应该封装领域知识,使这些领域知识对于客户端而言处于抽象状态。 例如,客户端应该能够在无需知晓派遣算法或如何管理无人机群的情况下安排无人机。
领域知识
域建模
领域
一个Bounded Context(界定的上下文)可能包含多个聚合,每个聚合都有一个根实体,叫做聚合根;
聚合
实体
值对象
模块
工厂
仓储
eg. 订单支付成功
清晰表述事件的概念
事件总线(EventBus)
发布订阅模式
最终一致性
领域事件
战术
策略性 DDD 提供一组可用于创建域模型的设计模式
领域驱动模型DDD
ER图建模
建模方法
提出了一种单向依赖关系,从而从逻辑上形成一种向上的抽象系统
整洁架构 - Clean Architecture
洋葱架构是由多个同心层构成,它们相互连接,并朝向代表领域的核心
洋葱架构
Command指的是增加/更新数据的处理。
Query指的是查询数据的处理。它不会造成数据的变更。
CQRS,全称Command Query Responsibility Segregation。直译过来就是命令查询的职责分离。
command 系统完成数据更新的操作后,会通过「领域事件」的方式通知 query 系统。query 系统在接受到事件之后更新自己的数据源
领域模型与数据模型的边界划分的更清晰些
目的
通过事件的形式通知 query 端系统,这就存在着一定的时间差
缺点
CQRS架构(命令查询的责任分离Command Query Responsibility Segregation)
外观模式 feignClient接口 包含DTO对象 独立模块
Facade
Query
其他操作
Request
Response
dto
interface接口层
1.接收restful接口
2.facade的实现层.rpc调用的实现
web层
controller
api接口层
负责接收web层指令,对领域层的逻辑调用。应用层负责将逻辑流程进行组装。而不负责具体的逻辑处理 可包含日志,消息,邮件等 负责对外转换
可根据CQRS设计拆分cammand与query
ApplicationService
ApplicationServiceImpl
GET/SET beanCopy
DO 和 VO或者DTO之间的转换
assembler
application应用层
负责逻辑处理
Aggregate(聚合)是一组相关对象的集合,作为一个整体被外界访问
聚合根(实体)
聚合(Aggregate)
实例的构造逻辑拆分
Factory
不一定有对应的PO
具备唯一标识
实体(Entity)
不具备唯一标识
值对象(VO)
同领域对象对应
规约模式提倡将验证和查询复用同一个逻辑单元
可规约模式(Specification)与CQRS冲突
仓库接口
领域对象
简化可以没有
service
domain领域层
围绕领域对象到PO的转换
converter
持久层对象
PO(persistant object:持久层对象)
仓库接口的实现
mapper
mybatis
mysql
redis
nosql
es
Repository
Infrastructure基础设施
视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。
对于服务层来说,它只从语义上定义:1-男性,2-女性,0-未指定,而对于展示层来说,它可能需要用“帅哥”代表男性,用“美女”代表女性,用“秘密”代表未指定。
restful 使用VO;
rpc使用DTO
DTO代表服务层需要接收的数据和返回的数据,而VO代表展示层需要显示的数据。
VO与DTO的区别
VO
展示层与服务层之间的数据传输对象。
对于一个getUser方法来说,本质上它永远不应该返回用户的密码
DTO与DO的区别
两者在本质上的区别可能导致彼此并不一一对应,一个DTO可能对应多个DO,反之亦然,甚至两者存在多对多的关系。
DO具有一些不应该让展示层知道的数据
为什么不在服务层中直接返回DO呢
DTO
领域对象,就是从现实世界中抽象出来的有形或无形的业务实体。
关系表 业务实体等也非一一对应
DO与PO的区别
DO
实体,和PO的功能类似,和数据表一一对应,一个实体一张表。
Entity
数据访问对象
DAO
多个PO的组合
BO
持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系
PO
COLA 架构(整洁面向对象分层架构 (Clean Object-Oriented and Layered Architecture))
明确区分应用程序,领域和基础结构三个层
依赖关系是从应用程序和基础结构再到领域
使用端口和适配器隔离它们的边界
六边形架构(端口和适配器)
分层架构
构造函数设为私有的
类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。
线程不安全
懒汉
线程安全
类加载时就初始化,浪费内存。
饿汉
synchronized
懒汉线程安全
安全且在多线程情况下能保持高性能。
B b = new B();
双重检查锁定失败的问题并不归咎于 JVM 中的实现 bug,而是归咎于 Java 平台内存模型。内存模型允许所谓的“无序写入”,
双检锁/双重校验锁(DCL,即 double-checked locking)
登记式/静态内部类
枚举
单例
工厂方法模式
抽象工厂模式
建造者模式
原型模式
创建型模式
实现被适配接口,拥有适配的引用
双向适配
适配器Adapter
装饰器模式
代理模式
外观模式
组合模式
享元模式
桥接模式
结构型模式
策略模式
模板方法模式
观察者模式
迭代子模式
责任链模式
命令模式
备忘录模式
状态模式
访问者模式
中介者模式
解释器模式
行为型模式
设计模式
一个类应该只有一个发生变化的原因
此原则的核心就是解耦和增强内聚性。
单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。
产生原因:(职责扩散)因为某种原因,某一职责被分化为颗粒度更细的多个职责了。
软件实体的行为是可扩展的,当需求变更的时候,可以对模块进行扩展,使其满足需求变更的要求
对扩展是开放的
软件实体进行扩展的时候,不需要改动当前的软件实体
对修改是关闭的
开闭原则(Open Closed Principle,OCP)
子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
子类中可以增加自己特有的方法
当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
任何基类可以出现的地方,子类一定可以出现。能保证子类完美替换基类
LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
里氏代换原则(Liskov Substitution Principle,LSP)
客户端不应该强迫依赖它不需要的接口
建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少
依赖几个专用的接口要比依赖一个综合的接口更灵活
接口隔离原则(Interface Segregation Principle,ISP)
从客户端代码调用框架代码,变成框架调用客户端代码。框架来定义接口,客户端来实现。
高层模块不依赖低层模块
抽象不应该依赖细节;细节应该依赖抽象。
TDD开发模式
依赖倒置原则的核心思想是面向接口编程。
依赖倒置原则更多是说,我们应该面向接口编程;好莱坞原则是说,低层组件将自己挂钩到系统上,由系统来主动调用。
“好莱坞原则”(Hollywood principle)
tomcat处理web请求的流程图,请求会经过 connector,coyote,engine,host,context,Servlet,层层传递最终传递到我们的应用程序里面来
tomcat
实践
依赖倒转原则(Dependency Inversion Principle,DIP)
面向对象五个基本原则(SOLID)
一个软件实体应当尽可能少的与其他实体发生相互作用
门面模式和调停者模式(中介者模式)实际上就是迪米特法则的应用。
第六原则-迪米特原则
尽量使用合成/聚合达到复用,尽量少用继承。原则: 一个类中有另一个类的对象。
第七原则 - 合成 / 聚合复用原则(Composite/Aggregate Reuse Principle,CARP)
"表达式"(expression)是一个单纯的运算过程,总是有返回值;"语句"(statement)是执行某种操作,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。
我要做什么
声明式
怎么做
命令式
与命令式编程区别
声明式编程
惰性执行
它要求你所有的数据都是不可变的,这意味着如果你想修改一个对象,那你应该创建一个新的对象用来修改,而不是修改已有的对象
数据不可变
合理性
可测试性
可移植性
并行代码
纯函数
无状态
无状态 、 数据不可变更
将电脑运算视为函数,强调函数计算比指令执行更重要
代码优雅 适合并发
函数式编程
这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
把生产者消费者模式,使用简单的API表示出来,并自动处理背压(backpressure)问题。
变化传递、基于数据流、变化传播
响应式编程是一种面向数据流和变化传播的编程范式
基于发布/订阅模式的数据处理规范 提供非阻塞背压的异步流处理标准的倡议
接口规范
如果生产者发出的信息比消费者能够处理消息最大量还要多,消费者可能会被迫一直在抓消息,耗费越来越多的资源,埋下潜在的崩溃风险。为了防止这一点,需要有一种机制使消费者可以通知生产者,降低消息的生成速度。生产者可以采用多种策略来实现这一要求,这种机制称为背压。
背压(back pressure)
Reactive Stream(响应式流/反应流)
链式代码
使用Netty,能够构建响应式编程的基础,加上类似lambda表达式这样的书写风格,能够完成类似WebFlux这样的响应式框架
响应式编程(Reactive Programming,RP)
编程范式
TCP 层之上的微服务层
代理变成了分布式的,它常驻在了应用的身边(最常见的就是 Kubernetes Sidecar 模式,每一个应用的 Pod 中都运行着一个代理,负责流量相关的事情)。
Service Mesh
虽然有主从节点之分,但是在恢复模式选举过程中仍可对外提供服务
AP
CP
CAP
RPC
一阶段记录undo-log并提交分支事务。
利用快照回滚
核心
AT 则是应用/驱动层实现的二阶段提交
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:提交异步化,非常快速地完成。回滚通过一阶段的回滚日志进行反向补偿。
如果 tx1 的二阶段全局回滚,则 tx1 需要重新获取该数据的本地锁,进行反向补偿的更新操作,实现分支的回滚。
两阶段提交协议的演变
支持本地 ACID 事务 的 关系型数据库
seata 使用全局锁避免脏写
AT
XA一阶段不提交分支事务,锁资源
利用数据库回滚
XA 是数据库层面实现的二阶段提交
分布式强一致性的解决方案,但性能低. 以为锁时间长
XA模式
业务无侵入
Try 预留资源 (如:库存服务的预占库存,支付服务的冻结部分账户余额)
Confirm 如果所有的事务参与者try 操作都执行成功了,就会调用所有事务参与者的confirm操作,确认资源。(如:库存服务的减库存,支付服务的扣减账户余额)
Cancel 如果有事务参与者在try阶段执行失败,就调用所有已执行try阶段成功的参与方的cancel方法,释放try阶段占用的资源(如:库存服务的释放预占库存,支付服务的释放冻结的账户余额)
TCC(Try Confirm Cancel)是应用层的两阶段提交,所以对代码的侵入性强,其核心思想是:针对每个操作,都要实现对应的确认和补偿操作,也就是业务逻辑的每个分支都需要实现 try、confirm、cancel 三个操作
如账户操作 : 下账 确认 取消下账操作
高性能分布式事务解决方案,适用于核心系统等对性能有很高要求的场景
TCC
长事务解决方案,适用于业务流程长且需要保证事务最终一致性的业务系统(第一阶段就操作DB,会存在脏读问题)
confirm 直接执行资源操作,(如:库存服务的减库存,支付服务的扣减账户余额)
cancel 回滚资源操作,这个地方的cancel与TCC中的cancel不一样,准确点说应该是回滚,(如:回滚库存服务的减库存操作,回滚支付服务的扣减账户余额的操作)。当然是业务层面实现的回滚,而非数据库事务层面的回滚
过程
异步更新的场景,并且对数据实时性要求不高的地方
消息发送是一个网络通信的过程,发送消息的过程就有可能出现发送失败、或者超时的情况。超时有可能发送成功了,有可能发送失败了,消息的发送方是无法确定的,所以此时消息发送方无论是提交事务还是回滚事务,都有可能不一致性出现。
事务消息和普通消息的区别在于事务消息发送成功后,处于 prepared 状态,不能被订阅者消费,等到事务消息的状态更改为可消费状态后,下游订阅者才可以监听到次消息。
区别
解决问题
1.事务发起者预先发送一个事务消息
2.MQ 系统收到事务消息后,将消息持久化,消息的状态是“待发送”,并给发送者一个 ACK 消息
3.事务发起者如果没有收到 ACK 消息,则取消本地事务的执行;如果收到了 ACK 消息,则执行本地事务,并给 MQ 系统再发送一个消息,通知本地事务的执行情况
4.MQ 系统收到消息通知后,根据本地事务的执行情况更改事务消息的状态,如果成功执行,则将消息更改为“可消费”并择机下发给订阅者;如果事务执行失败,则删除该事务消息
5.本地事务执行完毕后,发给 MQ 的通知消息有可能丢失了。所以支持事务消息的 MQ 系统有一个定时扫描逻辑,扫描出状态仍然是“待发送”状态的消息,并向消息的发送方发起询问,询问这条事务消息的最终状态如何并根据结果更新事务消息的状态。因此事务的发起方需要给 MQ 系统提供一个事务消息状态查询接口
6.如果事务消息的状态是“可发送”,则 MQ 系统向下游参与者推送消息,推送失败会不停重试
7.下游参与者收到消息后,执行本地事务,本地事务如果执行成功,则给 MQ 系统发送 ACK 消息;如果执行失败,则不发送 ACK 消息,MQ 系统会持续推送给消息。
处理流程
解决的是本地事务的执行和发消息这两个动作满足事务的约束
RocketMQ
在一次事务中需要发送多个消息的情况,保证多个消息之间的事务约束,即多条消息要么都发送成功,要么都发送失败
Kafka
Kafka 的事务基本上是配合其幂等机制来实现 Exactly Once 语义的,所以说 Kafka 的事务消息不是我们想的那种事务消息 RocketMQ 的才是。
中间件
事务消息
业务侵入
本地消息表
分布式事务
主观下线(Subjectively Down, 简称 SDOWN)指的是单个 Sentinel 实例对服务器做出的下线判断。
客观下线(Objectively Down, 简称 ODOWN)指的是多个 Sentinel 实例在对同一个服务器做出 SDOWN 判断,并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后,得出的服务器下线判断。(一个 Sentinel 可以通过向另一个 Sentinel 发送 SENTINEL is-master-down-by-addr 命令来询问对方是否认为给定的服务器已下线。)
下线
限流
服务降级
熔断
防止雪崩
高可用
队列
缓存预热是指系统启动后,直接查询热点数据并缓存
缓存预热
先删除缓存,再更新数据库
推荐
先更新数据库,再删除缓存
有多个并发的请求更新数据,你并不能保证更新数据库的顺序和更新缓存的顺序一致,那就会出现数据库中和缓存中数据不一致的情况。所以一般来说考虑删除缓存。
为什么是删除缓存,而不是更新缓存
缓存更新
网络抖动可能导致写缓存响应时间很慢,引起数据库事务阻塞
不要把写缓存操作放在事务中
缓存技巧
缓存策略
存在性检测
BloomFilter
对不存在的用户,在缓存中保存一个空对象进行标记,防止相同 ID 再次访问 DB。不过有时这个方法并不能很好解决问题,可能导致缓存中存储大量无用数据。
缓存空值
缓存穿透是指:查询的数据在数据库中不存在,那么缓存中自然也不存在。所以,应用在缓存中查不到,则会去查询数据库。当这样的请求多了后,数据库的压力就会增大。
缓存穿透
使用快速失败的熔断策略,减少 DB 瞬间压力;
使用主从模式和集群模式来尽量保证缓存服务的高可用。
部署 Redis Cluster(主从+哨兵),以实现 Redis 的高可用,避免全盘崩溃
增加缓存系统可用性(事前)
采用多级缓存方案(事中)
避免被流量打死
限流、降级、熔断方案 (事中)
缓存如果支持持久化 ,可以在恢复工作后恢复数据(事后)
针对多个热点 key 同时失效的问题,可以在缓存时使用固定时间加上一个小的随机数,避免大量热点 key 同一时刻失效。
缓存雪崩是指缓存不可用或者大量缓存由于超时时间相同在同一时间段失效,大量请求直接访问数据库,数据库压力过大导致系统雪崩。
缓存雪崩
锁住热点数据的 key,避免大量线程同时访问同一个 key
分布式锁
可以对部分数据采取失效前自动刷新的策略,而不是到期自动淘汰。淘汰其实也是为了数据的时效性,所以采用自动刷新也可以。
定时异步刷新
使用随机退避方式,失效时随机 sleep 一个很短的时间,再次查询,如果失败再执行更新。
缓存击穿是指,热点数据失效瞬间,大量请求直接访问数据库
缓存击穿
L1 进程缓存
L2 分布式缓存
L1 缓存
通过消息队列的发布、订阅机制,可以通知其他应用节点对进程内缓存进行更新。使用这种方案,即使消息队列服务挂了或不可靠,由于先执行了数据库更新,但进程内缓存过期,刷新缓存时,也能保证数据的最终一致性。
多级缓存更新
多级缓存
缓存
横切 取模 提高并发
分库分表
nginx
netty
应用
单 Reactor 单线程模型
单 Reactor 多线程模型
mainReactor
subReactor
因为subReactor也会执行一些比较耗时的IO操作,例如消息的读写,使用多个线程去执行,则更加有利于发挥CPU的运算能力,减少IO等待时间。
多线程下的Reactor模式
主从 Reactor 多线程模型
负责响应IO
我们如何知道IO就绪这个事件,谁来充当这个中间人?Reactor模式的答案是:由一个不断等待和循环的单独进程(线程)来做这件事,它接受所有handler的注册,并负责先操作系统查询IO是否就绪,在就绪后就调用指定handler进行处理,这个角色的名字就叫做Reactor。
Reactor反应器线程
Handlers处理器
生产消费模式没有队列缓存IO事件
和生产消费之模式对比
发布订阅模式有多个观察者/订阅者
与观察者模式(发布订阅)对比
模式对比
Reactor
Reactor模式里,操作系统只负责通知IO就绪,具体的IO操作(例如读写)仍然是要在业务进程里阻塞的去做的,而Proactor模式则更进一步,由操作系统将IO操作执行好(例如读取,会将数据直接读到内存buffer中),而handler只负责处理自己的逻辑,真正做到了IO与程序处理异步执行。所以我们一般又说Reactor是同步IO,Proactor是异步IO。
Proactor模式
多路复用
高并发
高性能
三高设计
分布式
授权框架
OAuth2
将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。
JWT
localStorage 或者 cookie
服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。
Token
session 是另一种记录服务器和客户端会话状态的机制
session 是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中
Session
安全设计
秒杀
单点
画布
消息总线
企业服务总线(ESB)
总线技术
决策引擎可以用于包括金融风控、内容风控、推荐营销、物联网监控等各个领域凡是涉及到使用规则或模型来做业务决策的场景都可以考虑使用决策引擎来实现
硬编码问题。规则调整要涉及到开发、测试、上线的流程,调整周期长。且规则逻辑维护到了代码中,难以传承,也容易造成生产中跑的规则和业务预想的规则有差异。当有大量规则或决策场景接入时,工作量巨大难以维护。
规则引擎
规则清单模式
策略引擎
决策引擎在规则引擎的基础上实现了更多元的决策方式,并引入了流程编排过程
复杂决策流模式
两个模式
Lua、Groovy、Javascript
领域特定语言 Domain Specific Language(DSL)
嵌入式脚本
Rete算法
drools
qlexpress
URule
fico
益博睿
决策引擎
govaluate(Golang)
SpEL(Java)
QLExpress(Java)
字符串表达式的执行
将规则和表达式以不同的方式组合起来,就构成了规则集、决策树、决策表等不同的组合形式(决策类型)。
决策表
决策矩阵( 交叉决策表)
决策树
评分卡
决策类型
决策流
规则组合
规则引擎(专家系统、决策引擎)
常用案列
服务端应用架构
批处理层(Batch Layer)
速度处理层(Speed Layer)
服务层(Serving Layer)
Lambda
不适用于批处理和流处理代码逻辑不一致的场景
Kappa架构的核心思想是通过改进流计算系统来解决数据全量处理的问题,使得实时计算和批处理过程使用同一套代码
流式重新处理历史的吞吐能力会低于批处理
缺陷
Kappa
数据应用层
ADS
数据服务层:Data Warehouse Service,DWS;
该层是基于DWM上的基础数据,整合汇总成分析某一个主题域的数据服务层,一般是宽表,用于提供后续的业务查询,OLAP分析,数据分发等。
DWS
数据中间层:Data Warehouse Middle,DWM
对通用的核心维度进行聚合操作,算出相应的统计指标
DWM
数据细节层:data warehouse details,DWD
该层是业务层和数据仓库的隔离层,保持和ODS层一样的数据颗粒度;主要是对ODS数据层做一些数据的清洗和规范化的操作,比如去除空数据、脏数据、离群值等。
该层通常会才采用一些维度退化方法,将维度退化至事实表中,减少事实表和维表的关联。
DWD
DW
数据运营层:Operation Data Store 数据准备区,也称为贴源层
业务库:sqoop定时抽取数据;实时方面考虑使用canal监听mysql的binlog日志,实时接入即可
埋点日志:日志一般是以文件的形式保存,可以选择使用flume来定时同步;可以使用spark streaming或者Flink、Kafka来实时接入
消息队列:来自ActiveMQ、Kafka的数据等
数据来源
ODS是后面数据仓库层的准备区
为DWD层提供原始数据
减少对业务系统的影响
主要功能
考虑后续可能需要追溯数据问题,因此对于这一层就不建议做过多的数据清洗工作,原封不动地接入原始数据即可
ODS
分层
Kimball维度建模
IBM-FSDM主题域模型划分
NCR FS-LDM主题域模型划分
雪花模型
星型模型
ADL数据应用层
主要职责是建设宽表模型、汇总表模型,比如商家宽表、商家时段汇总表等。主要作用是支撑数据分析查询以及支持应用所需数据。
MDL数据集市层
主要职责是划分分析主题、建设各个主题的基础指标,比如商家交易、用户活动等多个主题。这样针对同一个分析对象统一了指标口径,同时避免重复计算。
CDL层 数据组件层
主要职责是:业务主题的划分、数据规范化,比如商家、交易、用户等多个主题。这一层主要起到缓冲的作用,屏蔽底层影响,尽量还原业务,统一标准。
IDL层 数据集成层
主要职责是接入数据源,并做多数据源的整合
ODS层
美团数据建模规范
建模
事实表是指存储有事实记录的表,比如系统日志、销售记录等。事实表的记录在不断地增长,比如电商的商品订单表,就是类似的情况,所以事实表的体积通常是远大于其他表。
事实表 Fact Table
一般是配置表,比如枚举字段对应的中文含义,或者日期维表等;
低基数维度数据
高基数维度数据
维表层Dimension
数据质量管理
记录和管理数据仓库中数据的含义、格式、血缘关系等元数据的系统
元数据中心
数仓建模
Spark Streaming内置的Kafka Direct API (KafkaUtils.createDirectStream),实现精确Exactly-Once一致性
可靠的数据源端(数据可重复读取、不丢失)
基于Spark Streaming的Receiver模式,在Executor持续拉取kafka数据流kafka数据存储到Executor内存和WAL(预写日志)中WAL(预先日志)写入完成后,自动更新offset至zookeeper上
Spark Streaming 基于Receiver的Kafka高级API,实现At least Once语义
(1) 高级消费者API需要启用Receiver线程消费Kafka数据,相较于第一种增加了开销,且无法直接实现并行读取,需要使用多个Kafka Dtstream 消费同一组然后union。(2) 高级消费API在Executor本地和WAL存储两份数据<开启WAL不丢失机制>,而第一种Direct API仅在Executor中存储数据<offset存储到checkpoint中>(3) 基于Kafka Direct API的方式,因Spark集成Kafka API直接管理offset,同时依托于Kafka自身特性,实现了Exactly-Once一致性语义。因此在生产中建议使用此种方式!!
可靠的消费端(Spark内部精确一次消费)
基于RDD内存模型,启用多种一致性策略,实现Exactly-Once一致性。
序代码去重如果实时流进入到Spark消费端已经存在重复数据,可以编写Spark程序代码进行去重操作,实现Exactly-Once一致性。(1) 内存去重。采用Hashset等数据结构,读取数据中类似主键等唯一性标识字段,在内存中存储并进行去重(2) 使用Redis Key去重。借助Redis的Hset等特殊数据类型,自动完成Key去重。(3) DataFrame/SQL场景,使用group by/ over() window开窗等SQL函数去重(4) 利用groupByKey等聚合算子去重(5) 其他方法。。
幂等写入
事务写入
输出端
可靠的输出端(幂等性、事务)
Apache Spark的Exactly-Once机制
Apache Flink的Exactly-Once机制
分布式系统天生具有跨网络、多节点、高并发、高可用等特性,难免会出现节点异常、线程死亡、网络传输失败、并发阻塞等非可控情况,从而导致数据丢失、重复发送、多次处理等异常接踵而至。如何保持系统高效运行且数据仅被精确处理一次是很大的挑战。
当任意条数据流转到某分布式系统中,如果系统在整个处理过程中对该任意条数据都仅精确处理一次,且处理结果正确,则被认为该系统满足Exactly-Once一致性
正好一次exactly-once
至少一次at-least-once
最多一次at-most-once
Exactly-Once一致性语义
在自然环境中,数据的产生原本就是流式的。
低延迟、低容错
流计算系统在计算过程中,或是出现故障恢复计算后,流系统的内部状态和外部输出的数据应该处在一致的状态
定义
常见的非确定性计算包括使用了随机数、使用系统时间、字符串拼接等
流计算中的确定性指的是,给定相同的一组数据,重复运行多次或者打乱数据进入引擎的顺序,计算完成后将会输出相同的结果,否则就是非确定性计算。
确定性/非确定性计算
一致性
无重叠
滚动窗口
设置滑动步长、有重叠
滑动窗口
会话窗口
窗口
流处理
高延迟、高容错
批处理
流与批处理
大数据架构
架构设计
0 条评论
下一页