层:软件的逻辑单元。
每一层都有特定的功能。
组件被分配到不同层。
将系统按照之策拆分和组织。
上层依赖于直接下层。(下层不可以依赖于上层,不可以跨层访问)
CS架构是典型的两层架构,分为Client客户端和Server服务端。
客户端运行于客户的终端,服务端处理应用服务、业务逻辑和数据存储。
客户端和服务端通过网络交换信息。
BS架构同样是典型的两层架构,分为Browser浏览器和Server服务端。
Browser运行于各种浏览器的瘦客户端。
客户端和服务端通过网络交换信息。
Presentation:与用户交互和呈现信息(UI)。
Domain:业务逻辑。
Data:数据存储。
隔离业务复杂度和技术复杂度。
解决不同层的问题,可以采用不同的技术栈。
每层变化速度不一致。
防止错误传播。
降低错误影响。
防水仓设计。(熔断)
本层功能内聚。
自主决策。
只有本层的知识。(知道的越少,泄密的可能性越小,受影响的可能性变小)
每一层的任何变更最多影响自身和上一层。
专注自身功能,其他层的影响被屏蔽。
符合单一职责原则。
每一层只依赖于下一层。
单向依赖。
通过接口交流。(接口聚合、依赖反转)
功能扩展,影响仅限本层。
内聚性,易于横向扩展。
独立部署,易于纵向扩展。
每层定义了清晰的边界,扩展发生在不同边界,符合开闭原则。
分工合作,开发者关注点集中。
每一层可以依据接口并行开发。
每一层功能单一,代码易于理解。
每层对外提供固定接口,可以直接测试接口。
分层测试。(比如Springboot分为@DataJpsTest和@WebMvcTest测试)
分层必定引入新的通信开销。
层信息不能泄漏,导致每层都有数据转化发生。
不能跨层访问,无法减少调用链路。
Full stack全栈少且难以培养。
跨组织提高沟通成本。
任何变更可能都需要多层参与。
越向外越具体,越朝内越抽象。
外圈是软件,内圈是规则。
依赖关系只能从外向内。
高层表示规则,底层实现细节。
逻辑内聚自治分组。
依据组织职责分工。
根据每层的需求各自选定。
借鉴成功案例。
部署方式局限,选定技术栈。
层对外暴露的接口,隐藏实现细节。
实现依赖于抽象,抽象不可依赖于实现细节。
代码不跨层调用,只依赖于直接的下一层。
集成前做单元测试、功能测试。
根据接口和技术栈确定集成方式。
集成联调。
Model:Domain model和业务逻辑。
View:展示数据和用户交互。
Controller:接收输入并转化为对model的操作,将model转化为view能展示的数据。
是MVC的派生变种。
View通过Presenter获得数据而非Model。
Presenter层充当了桥梁,双向绑定。
Model:除了自身也包含部分Controller功能。
ViewModel:View的模型、映射、显示逻辑和绑定器。
View:将ViewModel展示在特定界面。
BFF:Backend for frontend,一般用于微服务架构。
避免了终端与多个服务的交流。
比较常见的就是GraphQL。
源系统发送消息通知其他系统状态改变。
接收方响应非必须。
发送Event逻辑与处理Event逻辑无依赖,独立变化。
双方解耦,各自扩展。
(例如:使用MQ)
该模式下,生产者把详细状态数据添加到事件中,如此对消费者而言,不需要再访问事件生产者服务了解详情就可以完成后续任务。事件生产者需要仔细衡量,提供足够的信息以便对已知和未知订阅者有用。
该模式的好处显而易见,通过数据冗余增加了系统的可恢复性能力,因为消费者不依赖生产者就可以独立提供服务,同时可以降低延迟,不需要远程调用生产者系统,也不用担心生产者系统来自不同消费者过多的查询工作负载;但副作用也很明显,大量的副本,带来冗余的存储使用,同时增加了消费者从所有事件中维护一致性状态的复杂性(需要依赖事件顺序)。
源系统推送信息及变化。(依赖者生成信息副本)
容错(fault tolerance)。(源系统不可用时,依赖者可以使用副本)
依赖者维护信息副本和变化。(数据的冗余问题、数据一致性问题)
提高整体性能。(依赖者直接使用信息副本)
事件驱动架构模式是一种异步
分发事件的架构模式。
用于高扩展且低耦合的系统。
事件为核心,一系列解耦的、单一功能的事件处理器。
针对不同的IO event分配不同的handler。
Selector:监控哪些Channel有IO event。
SelectionKey:维护IO event的状态和绑定的handler。
nginx的工作进程一直监听端口并等待event。
event是由新建立的连接所触发。
所有的连接都会被分配到一个对应的状态机。
Non-blocking。
存在业务流程。
多步骤。
统一协调,集中调度。
事件分层次。
Event Queue:
源系统将event发送到queue。
Event处理系统从queue中消费event。
是源系统和事件处理系统的连接点。
Queue只关心事件的传输,对事件的处理不关心。
Event Mediator:
中介从queue中消费原始事件,并分配、协调各个执行步骤。
对应业务处理的每个步骤,产生待处理事件(业务事件)。
将待处理事件异步分发到不同的通道(channel)。
无具体业务逻辑,只知道有哪些步骤。
Event Channel:
Mediator向channel发送消息,processor从channel读取。
某种类型的业务事件(消息)的聚合。
多个processor可以监听同一个channel。
Event Processor:
监听channel的event。
具体的、单一功能的业务逻辑单元。
处理器之间无关联性。
所有处理器合作形成完整业务流程。
无中心控制器。
轻量的消息代理将消息串联成链状。
分发至事件处理器组件。
事件处理器独立运作。
Broker:
是轻量级的代理。(中介是知道业务流程的,Broker不需要知道业务流程)
无业务逻辑的简单消息分发。
源系统将event发送至代理(通道)提供processor消费。
事件通道可以是queue、topic或两者组合。
Event Processor:
监听channel的event,判断是否处理。
处理一个事件,并发送回Broker一个事件,标明其行为。
事件处理器,单一业务逻辑。
处理器无关联性,不构成业务逻辑链。
架构能否在不断改变的使用场景下快速响应。
事件处理器组件目的单一、高度解耦,可以独立变化。
代理拓扑结构比中介拓扑结构调度会更容易。(事件中介与事件处理器是耦合的,代理模式完全解耦)
高度解耦,独立变化。
横向扩展。(不同组件的运行节点数均可自行调整、组件本身可以自行决定是否再拆分实现)
纵向扩展。(计算密集型还是内存密集型,按需调整)
细粒度的事件处理器有利于提高性能。
整体架构是异步并行有利于提高性能。
高度解耦的事件处理器组件让整体部署相对容易。
单元测试无差异。
集成测试难。(组件众多、异步)
分布式部署,复杂性高。
异步,对程序员要求高。
异常处理难。
代码可读性较差,代码复杂度高。
分布式消息(事件)传递会降低性能。
(1)短信验证码异步发送
(2)电商订单生命周期管理
(3)数据同步消息广播
微内核,就是核心功能
、资源封装
。
资源封装(提供硬件接口、系统资源访问接口、环境/上下文Context访问接口、系统事件接口)。
定义插件规范(使用场景、规则、条件)。
核心功能(支持系统运作的最小功能集)。
职责分离(通用流程由核心系统定义、核心定义规范,插件具体实现)。
插件模块是核心系统能力的扩展(遵循核心系统规范、实现其逻辑外延和业务逻辑)。
插件应该遵循单一职责原则(专注于其独立功能、只能通过核心提供的接口操作系统资源)。
插件间无依赖(尽可能避免依赖其他插件,只依赖于核心)。
隔离性(插件不能影响核心)。
插件注册:
需要获取插件可用性。
统一的获取插件的方式。
检查信息抽象规范(名称、数据规范、访问协议)。
插件连接:
自定义连接方式(OSGI,点对点绑定,依赖注入)(web service、message等)。
通信规范(标准规范配合版本策略、自定义规范配合Adapter)。
系统核心封闭。
插件提供开放性。
整体系统可持续升级。
核心系统可以关闭插件。
错误的传播范围有限。(插件内的错误不会被传播到核心系统)
各自独立升级改进。(可能有兼容性问题)
核心系统保持稳定,将变化尽量隔离在插件层。
插件根据核心提供的接口和规范来提供丰富的功能。
整体保持开放,持续进化。
核心系统和插件系统可以分开测试。
插件可以运行在模拟环境。
通过简化核心系统,提高其性能。
插件按需加载,降低资源消耗。
可以动态关闭插件以保护核心系统。
插件可在运行时动态添加到核心系统。
减少核心系统停机时间。
需要分离核心功能与插件功能。
插件需要可以热插拔。
需要专门注册协议和通信协议。
主要用于开发产品,不考虑扩展性。
不以可扩展性见长。
但是,可以结合其他模式获得扩展性。
定义核心功能。(实现MVP最小可行产品)
封装系统资源。(插件通过接口访问)
开放集成点。(支持与插件的集成和通信)
核心系统提供的接口及版本。
提供上下文(context)、环境参数。
提供回调(call back)、钩子(hook)、事件(event)。
集成规范。
同步/异步。
本地/远程调用。
数据格式。
基于注册表获取插件信息。
装载机制。(启动器/运行期装载、内存/远程装载)
装载条件。(触发条件符合才加载)
Eclipse开发者可以提供各种插件,为Eclipse提供更多额外的功能。
专家系统。(将人类专业知识转化为程序,用程序来解决专业问题)
Rule。(Condition、Action)
Rule Engine。(基于输入数据执行规则的专家系统,如果满足条件则执行对应的任务)
通过将不同应用接入ESB以扩展系统功能。
ESB是核心系统。
应用就是插件。
应用之间无依赖,都与ESB沟通。
通过BPM组合不同子流程形成新的流程扩展系统功能。
BPM是核心系统。
子流程是插件。
子流程间无直接依赖。
Nginx通过module为nginx添加新功能。
nginx是核心,module是plugin。
开源nginx在编译期指定要添加的模块。
nginx plus可以动态添加或移除module。
Servlet实现核心功能。
插件:(Filter:针对request和response的操作;Listener:Servlet生命周期事件并执行相应命令)
SpringMVC,Struts众多功能都是基于Listener和Filter实现。
同步调用变成异步调用。
生产数据与消费数据分离。
协调不同处理速度。
生产者是系统运转的动力。
为下一环节产生待处理的工作/数据。
与消费者关系:重点在如何将数据发送到容器、对消费者无依赖、不关注消费者的how/when。
发送顺序问题比较难处理。
从容器中获得数据,并按照自身业务逻辑处理。
依赖于容器,不直接依赖于生产者。
向容器确认数据已经被消费。
处理后的数据:从容器中删除(queue)、保留在容器中(topic)。
消费顺序问题比较难以处理。
容器是消费者与生产者的交汇点。
容器担当了保管数据的责任。(容量、保存时间、读写效率)
是生产者和消费者的差速器。
数据分配策略。(发给哪个消费者)
数据不堆积。(容器中数据不会溢出)
消费者无资源浪费。(最好是消费者的消费能力刚刚好)
调度算法。(多个消费者消息路由问题)
EDA架构模式:
事件触发流程;
重点在事件本身;
不解决事件/消息如何传递的问题。
生产者消费者模式:
强调的是从生产端到消费端的全流程;
从抽象程度来看EDA更抽象。
消费策略决定消费者如何消费数据。
消费方式:push(容器推送给消费者)、poll(消费者从容器拉取)。
分发方式:topic(消息一对多)、queue(消息点对点)。
事务性问题。
producer、consumer和container均可独立变化。
producer和consumer互无依赖。
无事务系统时,对三者都无约束。
异步运作。
producer、consumer和container均可分开部署。
producer、consumer和container自成分布式系统。
各自按需扩展。
通过container来平衡生产速度和消费速度。
通过动态调节producer和consumer数量,以平衡系统差速。
减少资源浪费。(避免生产过多数据,避免数据堆积在容器)
生产和消费速度不确定时,达到削峰填谷的效果。
削峰:生产数据超过消费数据,缓存数据。
填谷:生产数据的速度下降,消费端消费速度不变。
单点流量增加不影响系统稳定性,整体系统运行于匀速状态。
定义:生产者放入/发送到容器的单体、容器内存储的单体、消费者消费的单体。
从业务逻辑推导:数据单元具备业务含义、数据单元的颗粒度适中。
完整性保证:传输过程中数据单元不可缺失。
无依赖:数据单元是独立的,两个数据单元之间互不影响。
生产/发送,对发送成功的确认:
只管发送不管是否发送成功;
发送之后需要同步知道是否发送成功;
异步发送之后不需要同步知道是否发送成功,而是通过回调来知道是否发送成功。
数据单元变成用于网络传输的数据。(JSON、Avro、Thrift、Protobuf)
数据单元的时间、空间的独立性。
超时、错误、重试等。
推(pust):
由container发起,将数据单元推送给consumer;
consumer可以尽快获得数据。
如果推的太快,可能会导致consumer崩溃。
拉(pull):
由consumer主动发起,一直不停的询问container。
获取数据有一定的滞后,并不是实时。(Long polling)
好处就是,consumer可以自己控制拉取数据的数量。
Queue:点对点的模式,不可重复消费。
Pub/Sub:发布订阅,Topic模式,1对N,消息相当于可重复消费。
将网络数据流还原为数据单元。(JSON、Avro、Thrift、Protobuf)
Commit log(记录消息读到哪里了)。
ACK。(消息处理完毕之后,给容器一个应答)
批量/单体消费。
同步/异步消费。
保证幂等性。
Replica(数据复制)。主从节点复制、分区。
Non-replica(不复制)。只存meta data,读取转移。(读的时候会判断,数据和真实存储的地方有个关联关系,类似做了index索引)(用的较少)
数据单元的存储结构。
时效性。(不能一直保存下去)
分配数据单元到consumer。
Producer保证Consumer至少收到一次数据单元。
从业务分析Container中可能有重复数据单元。
Consumer实现要防重或幂等。
Producer保证Consumer最多收到一次数据单元。
从业务分析Container中可能会丢失部分数据单元。
Consumer可能无法读取到所需数据单元。
或许要引入第三方数据比对验证程序。
Producer保证Consumer收到并仅收到一次数据单元。
从业务分析Container中有且仅有一份数据单元。
Consumer无需防重。