个人博客
Software Architecture Patterns》是 Mark Richards 2015 年出的一本小册子,对常用的架构模式进行了一个简单梳理,书中列了 5 种:
- 分层(Layered)
- 事件驱动(Event-Driven)
- 微内核(Microkernel)
- 微服务(Microservices)
- 基于空间(Space-Based)
Mark Richards 后续又出了两本书,《Fundamentals of Software Architecture》、《Software Architecture: The Hard Parts》——参见 读后感。
Layered Architecture
单体架构,将应用这个整体分解为不同的层次,在不同的层次处理不同的问题,即关注点分离原则的一种实际运用。在社会生活的各方面同样可以观察到对该原则的广泛运用,比如组织中的科层制——总经理就不会去厂房打螺丝。软件设计的这种原则往前追溯,可能就借鉴自人们在社会生活中长期实践所总结出的经验。
分层架构在网络中得到了最成功的运用——OSI 分层模型(OSI 模型发展过程中遇到的问题及其解决方案,对其他运用分层思维解决问题的领域具有良好的借鉴意义),在 Web 应用中也广为人知,常用的 MVC 架构即是把应用分解为 模型-视图-控制器 三层,模型层对接业务模型,视图层对接用户界面展示,控制器层居中粘合模型和视图。
典型的 Web 分层架构包括 4 层:
- 表现层,负责界面展现,面向 PPT 的说法叫负责人机交互接口;
- 业务层,负责实现业务逻辑;
- 持久层,负责把数据持久化,掉电不丢失;
- 数据库层,应用使用的数据库。
请求必须沿着一定的方向逐层传递,不能跨层——每一层都是 closed 的,这样做的好处是隔离了变化,某一层的变化只影响相邻层,而不会影响其他层。特殊情况下,某层也可以设置为 open —— 即允许被跨越。
总评价:
- 全局敏捷性,低,单体本质上不能快速响应变化,同时各组件可能紧耦合在一起;
- 易部署性,低,设计不良的情况下一个小的改动需要重新部署整个应用;
- 易测试性,高,因为严格分层,所以可以方便地进行 mock/stub;
- 性能,低,请求必须经过所有层,而可能在某些层没有处理逻辑;
- 易伸缩性,低,扩展时需要进行整层扩展或整个应用整体扩展;
- 易开发性,高,该架构广为人知且团队组织架构通常也分为了前端/后端/DBA,可以很好地映射并组织工作。
Event-Driven Architecture
大多数的应用是请求响应式的,而事件模型是获取相应事件并采取相应的行动。事件驱动架构是一种分布式异步架构,具有高可扩展性。
事件驱动架构有两种拓扑:中介拓扑、代理拓扑。
中介拓扑(mediator topology)
由中介来规划事件应该发送到哪些事件处理器,从而控制整个事件处理过程,如 BPM 业务流程流转。
架构组件包括:
- 初始事件,启动整个事件过程的事件;
- 事件队列,存放初始事件的队列;
- 事件中介,核心组件,负责编排整个处理过程,根据掌握的事件处理步骤生成事件,并发送到对应的事件通道,可以根据领域不同划分出多个;
- 事件通道,存放(需要事件处理器处理的)事件,通常也是队列;
- 事件处理器,监听事件通道,处理事件,并向中介返回已完成事件处理的响应,不会告知对系统其他部分做了什么。
简单的事件中介有 Spring Integration、Apache Camel、Mule ESB,复杂的有 Apache ODE、Oracle BPEL。一种具体的使用方式是将事件分为简单/复杂/困难,每个事件都通过一个简单事件中介,简单事件中介查询事件分类来决定是自己处理还是委托给更复杂的事件中介。
代理拓扑(broker topology)
没有一个中心,使用轻量级消息中间件来充当各个事件处理器的事件代理,如 RabbitMQ、ActiveMQ、HornetQ 等。
架构组件包括:
- 初始事件,启动整个事件过程的事件;
- 事件代理,轻量级消息中间件,一般采用发布-订阅模式;
- 事件处理器,监听并处理事件,生成待处理事件发布到事件代理;
- 待处理事件,等待事件处理器处理的事件。
相比于中介拓扑,代理拓扑没有一个中心组织者,只有各自独立的参与者,但参与者会在传播介质(事件代理)中广播自己对系统做的改动,由其他参与者根据事件做出自己的反应。
因为事件驱动架构是分布式的,所以也需要考虑分布式架构所面临的问题,如对端的可用性、响应性、重连等。另外,从架构本身来说也有两个困难:缺乏原子事务、各事件处理器之间的契约(事件具有自己的结构)的创建、维护和管理。
总评价:
- 全局敏捷性,高,各事件处理器相互独立,耦合性低,变化的影响被局限在一个有限的范围;
- 易部署性,高,各事件处理器相互独立,耦合性低,物理分开方便单独部署;
- 易测试性,低,一个案例需要考虑若干事件、若干组件;
- 性能,高,异步带来高性能;
- 易伸缩性,高,各组件独立;
- 易开发性,低,异步增加复杂性以及架构复杂后带来的更多的错误类型。
Microkernel Architecture
微内核架构一般又称为插件架构,是一种单体架构,架构组件包括:
- 核心系统,系统运行的最小功能集;
- 插件,独立组件,用于扩展系统功能,可以方便地插拔。
插件和核心系统之间的通信通常是点对点的,插件之间相互隔离,只和核心系统交互。插件可以是基于编译的,也可以是基于运行时的。
一个关键问题是:核心系统如何发现插件。
常见方案是通过一个注册中心,在注册中心中存放插件的信息,包括名称、数据契约、远程访问协议细节。对于核心系统和插件之间的契约,一般是跨插件域的标准契约,包括从插件返回的行为、输入数据、输出数据。
插件要接入核心系统,可以通过多种方式,如 OSGi、消息、REST 等。某种程度上,博主当前负责的系统也是一个微内核架构,通过动态注册服务来扩展系统的能力。
微内核架构和开源思想可以说是天作之合,这种架构天然鼓励社区参与,对于商业初创产品来说也是合适的——先聚焦核心功能,再逐步推出扩展功能。不只 Linux、Eclipse、Vscode、Nginx 等软件界的明星项目,这种架构思想在硬件架构中也是通用的,继续扩大观察面会发现生活中遍布这种设计思想。
总评价:
- 全局敏捷性,高,插件隔离性好,可以快速变化;
- 易部署性,高,支持运行时部署,停机时间极大缩短;
- 易测试性,高,插件互相独立;
- 性能,高,可以只启用需要的功能;
- 易伸缩性,低,还是单体架构;
- 易开发性,低,契约版本管理、插件注册、插件粒度、接入方式带来更大的复杂性。
Microservices Architecture
留白,春节再续…
Space-Based Architecture
以传统的 Web 应用为例,在面对大并发量时,第一个迎接流量洪峰的是 Web 服务器,可以很容易地横向扩展;往下来到应用服务器,应用服务器稍微复杂一点,也可以扩展;最后,洪峰来到了数据库,对数据库做横向扩展通常比较困难。也就是说,最后是数据库的并发事务处理能力,决定了整个系统的最大并发处理能力。当然也可以通过提前的分库分表,通过 apache-sharding 之类的方式来支持更大的并发能力,但从架构上说,难以灵活应对极端并发场景的缺陷是天生的。
于是有了基于空间的架构模式:通过删除中央数据库作为系统中的同步约束,代之以利用复制的内存数据网格,来实现高可伸缩性、高弹性和高性能。
架构组件包括:
- 处理单元,通常包含程序逻辑、内存数据网格、复制引擎;
- 虚拟化中间件,核心组件,负责管理数据同步和请求处理,包含消息网格、数据网格、处理网格和(管理处理单元的)部署管理器。
虽然在该架构中不再要求有一个中央数据库,但还是可以增加一个,作为初始化加载数据时的来源,和异步持久化数据时的地方。
总评价:
- 全局敏捷性,高,快速响应负载,应用小也可以快速响应代码变更;
- 易部署性,高,借助工具;
- 易测试性,低,要模拟这种架构提供的易伸缩性和弹性比较复杂;
- 性能,高,基于内存和缓存机制;
- 易伸缩性,高,没有中央数据库的约束;
- 易开发性,低,架构复杂不便于理解,同时需要借助工具。
The End
各种架构模式并不是排他的,在实际使用时常常可以根据需要混合使用。
有模式,就有反模式,任何实际工作都不可能完美匹配模式场景,所以不能拘泥于理想的理论描述,而必须结合实际情况,在运用模式无法达到想要效果时进行必要的调整。
需要记住的是,调整一定有代价,架构设计就是权衡是否值得为了一个设计目标而在一定程度上放弃另一个目标。