人类的智力存在上限和无法扩容可能是人类文明发展的最大障碍。为了解决这一问题,人类发展史上所有的科技发明,无一不是想方设法来扩容脑力。软件作为一种模仿人类脑力活动的“生命体”,在其发展早期,也遇到类似问题,也就是Frederick P. Brooks, Jr.教授著名的“人月神话”观点。为了解决这一问题,软件科学发展很多方法论和技术,比如分布式,比如我们今天的主题:软件架构和模式。
软件架构概念
什么是软件架构
学习一个概念,如果知道这些概念要为了解决什么问题,解决谁的问题,对深入掌握它有事半功倍的效果。对于软件架构这个概念,业界并没有一个统一、标准的定义,如果要看透看全面,我们也需要从不同视角来了解它。
什么是软件架构,不同视角下,有着完全不一样的解读。
从组成的角度看:架构=组件+交互:软件系统的架构将系统描述为计算组件及组件之间的交互。这是《软件构架实践》作者Bass、《企业应用架构模式》作者Martin Fowler等人的观点。
从决策的角度看:架构=重要决策集:架构表示对一个系统的成型起关键作用的设计决策,架构定系统基本就成型了,这里的关键性可以由变化的成本来决定。这是UML的创始人Grady Booch等人的观点。
从需求的角度看:架构实际上解决的是人的问题,系统架构的目标是解决利益相关者的关注点。
从演化的角度看:架构是用于管理复杂性、易变性和不确定性,以确保在长期的系统演化过程中,一部分架构的变化不会对架构的其它部分产生不必要的负面影响。这样做可以确保业务和研发效率的敏捷,让应用的易变部分能够频繁地变化,对应用的其它部分的影响尽可能地小。
什么是好的架构
首先,找到所有的利益干系人,并为他们服务。不同干系人看待架构有不同的视角,这就是架构视图。架构视图分很多种,常见的有逻辑架构视图、开发架构视图、运行架构视图、数据架构视图和物理架构视图等,这其中最常用的是逻辑架构视图和物理架构视图。逻辑视图用于指导设计开发,物理视图用于指导部署运维。
其次,发现并解决利益干系人关注点背后真正的问题。发现问题永远都比解决问题来的更加重要。
最后,符合康威定律,软件架构要与组织架构要一致,只有这样才能够让架构落地并推进。如果不一致,要么调整软件构架,要么调整组织架构。
软件架构模式
模式(Pattern)其实就是解决某一类问题的方法论。而软件架构模式就是解决某一类软件组织构架问题的现成形式。现有的软件架构模式如下图所示。其中分层架构、事件驱动架构、微服务架构、面向服务架构等在阮一峰的《软件架构入门》一文有较详细的介绍。但是还有不少问题没有谈及,比如,这么多架构模式如何选择?每种架构模式解决什么问题?适用什么场景?本节想对这块重点做下比较说明,算是一个补充。
分层架构模式
适用场景:
复杂系统的各个部分需要独立开发和演进。开发人员的关注点分离,系统模块可以独立地开发维护。
解决问题:
如何保证软件模块可以独立开发和演进,模块间尽可能少的交互,模块支持可移植性、可修改性和可重用性等质量属性?
解决方案:
为达到关注点分离,软件以层为单位进行划分,层是一组提供内聚服务模块组合。层通过公共接口对外提供服务,层级调用必须是单向的。
模式缺点:
- 分层增加了系统的成本和复杂性;
- 额外的分层逻辑对系统性能有一定影响。
Broker架构模式
适用场景:
分布式系统。其提供的服务分布在不同服务器上。它们的互连关系、交换信息方式等交互复杂。系统对服务的可用性有较高要求。
解决问题:
如何实现分布式软件系统,让用户不需要感知服务提供方的位置,并且用户和服务提供方之间的绑定关系可以动态变化?
解决方案:
引入一个Broker组件,解耦客户端和服务端。服务端注册自己到Broker,通过暴露接口的方式允许客户端接入服务。客户端是通过Broker发送请求的,Broker转发请求道服务端,并将请求的结果或异常回发给客户端。通过使用Broker模式,应用可以通过发送消息访问远程的服务。
这一架构模式允许动态的改变、添加、删除服务端,从客户端的角度,这些都是透明的。
- Client -- 需要访问远程服务的客户端
- Server -- 服务的提供者
- Broker -- 类似于消息的转发器,负责控制和管理集群,Server 启动时向 Broker 注册,从而 Broker 在接到 Client 的消息后可以得知要将消息转发给哪个 Server,然后在 Server 做出应答或发生异常后再将回应通知给 Client
- Client_Proxy -- Client 端的代理层,对 Client 隐藏连接、通讯等底层服务,让 Client 在使用远程服务时如同使用本地服务一样简单,他维系了 Client 与 Broker 的通信和连接
- Server_Proxy -- Server 端的代理曾,同样,他接收请求、解包消息,让 Server 与 Broker 的通信和连接被隐藏
- Bridge -- 用于多个集群的复杂网络,协调多个 Broker 的数据、消息等工作
模式缺点:
- broker作为间接层,增加了clients和servers间的延迟,而且可能是通信瓶颈。
- broker是单点失效节点。
- broker增加系统复杂性。
- broker是易于成为安全攻击的目标。
- broker难以测试。
MVC架构模式
适用场景:
包含有人机互动介面(UI)的系统。用户希望从不同视角来查看数据,如柱状图、饼状图等。
解决问题:
如何实现用户界面(UI)与应用逻辑的分离,并保证系统能响应用户不同的输入或对应用数据的修改?当底层应用数据变化时,用户界面的多视角如何创建、维护和协调?
解决方案:
model-view-controller模式将系统划分为3个组成要素:
- Model(模型):应用的数据或状态,以及应用逻辑。
- View(视图):按用户请求显示底层数据,提供给用户的操作界面,是程序的外壳。
- Controller(控制):协调管理model和view间的交互,将用户行为转化为对模型的修改或者对视图的修改。
模式缺点:
- 引入一定的复杂性,不适用简单的UI系统。
- 一些UI工具包不支持MVC模型。
管道过滤器(Pipe-And-Filter)架构模式
适用场景:
处理数据流的系统。输入数据后,经过一系列的数据加工转换后输出。数据转换动作重复、独立、可复用,支持并发。
解决问题:
如何将系统切分为可复用、松散耦合的组件,并且组件间的交互足够简单?如何保证组件可以弹性组合,组件通用、低耦合、可复用,并且可并发执行?
解决方案:
管道和过滤器(Pipes and Filters)架构模式由过滤器和管道组成。每个处理步骤都被封装在一个过滤器组件中 , 数据通过相邻过滤器之间的管道进行传输。每个过滤器可以单独修改 , 功能单一 , 并且它们之间的顺序可以进行配置。Unix shell管道和编译器都是典型的例子。
- 过滤器:一种组件,其功能是从输入端口读入数据,进过数据转化后,再将数据写入输出端口输出。
- 管道:一种连接器,其功能是将数据从一个过滤器的输出端口传输到另一个过滤器的输入端口。管道的输入源和输出地是单一的。管道不修改其传输的数据。
模式缺点:
- 共享状态信息或者昂贵或者不灵活。
- 数据转换额外开销。
CS架构模式
适用场景:
分布式系统。系统中大量的分散的客户端对共享的资源和服务进行访问。
解决问题:
如何在资源和服务分布在多个物理服务器的分布式系统中,通过对共享资源进行集中管理,提高系统的可维护性和可复用性,改进其可扩展性和可用性?
解决方案:
CS架构模式由client和server2个要素组成。server提供服务,client请求server的服务,系统交互由client发起。系统可以有一个中央server,也可以是多个分布式server。
- Client:请求Server服务的组件。拥有描述请求服务的接口。
- Server:提供服务给Client的组件。拥有描述提供服务的接口。
模式缺点:
- Server可能是系统性能瓶颈。
- Server可能是单点失效节点。
- 系统一旦部署,调整代价大。
P2P架构模式
适用场景:
分布式系统。系统中每个组件的地位对等,都可以主动发起交互和对外提供服务。
解决问题:
地位对等组件间的互连公共协议如何实现?如何保证系统的高可用性和可扩展性?
解决方案:
P2P模式由peer和connector 2个要素组成。所有的peer地位对等,不是单点失效节点。peer间通过request/reply connector连接并直接交互。
Peer:运行在网络节点上的独立组件。特定peer组件(如图中的超级节点ultrapeer)可以提供路由、索引、查找等功能。
Request/reply connector:用于连接peer网络,查找其他peer节点和调用其他peer节点上的服务。
模式缺点:
- 数据一致性、数据有效性、数据备份和恢复,安全等实现方案复杂。
- 对于小的P2P系统,性能、可用性等质量目标较难以达到。
面向服务架构(SOA)模式
适用场景:
异构的分布式系统。服务提供方提供服务,并由用户使用。用户不需要了解服务的实现细节,即可理解和使用它们。
解决问题:
一个系统中,服务组件运行在不同的平台,由不同的编程语言开发,由不同的组织提供,分布在各网络节点中,如何保证这些组件的互操作性?
解决方案:
采用中央管理模式来确保各服务能够交互运作,服务间通过连接件交互,通过网络对外提供或使用服务。SOA模式由如下几个要素组成。
- 应用服务(Service):提供服务或调用服务。即服务提供者或服务使用者,或两种角色兼具。
- 企业服务组件(Enterprise Service Bus):一种在服务提供者和使用者之间传输、转化消息的中间件。
- 服务注册:服务提供者通过它注册服务,服务使用者通过它在运行时发现服务。
- 编排服务:基于商业流程和工作流,协调服务提供者和服务使用者间的交互。
- 连接件:服务通过连接件进行相互通信和操作。分为SOAP、RESTful、Asynchronous messaging三种实现协议。
模式缺点:
- SOA系统构建复杂,成本高。
- 无法控制服务的演化。
- 中间件会引入系统性能开销,服务可能会成为系统性能瓶颈。
微服务架构模式
适用场景:
分布式系统。基于服务的企业应用,支持网页、手机客户端等不同的访问方式。
解决问题:
单块架构的应用可能过于庞大和复杂,难以维护,严重影响效率。而且无法进行分布式部署和弹性扩容。
解决方案:
微服务架构模式是一种将商业逻辑切分为一系列可以独立开发和部署的服务的架构。其中各项服务都拥有自己的进程,利用轻量化机制或RESTful接口实现通信。这些服务围绕业务功能建立而成,且凭借自动化部署机制实现独立部署。这些服务匹配一套最低限度的中央式管理机制,且各服务可通过不同编程语言编写而成,拥有自己独立的数据库,由不同的开发团队开发。
模式缺点:
- 微服务有额外成本的,需要搭建框架、发布、监控等基础设施。
- 微服务架构可能带来过多的操作;因为分布部署跟踪问题难。
- 微服务应用是分布式系统,会带来固有的复杂性。
- 随着微服务数量不断增加,其管理复杂性也将增加。
发布订阅架构模式
适用场景:
系统中有大量的独立的数据生产者和数据消费者,数据生产者和消费者的数量和种类动态变化。它们通过数据交互,但数据在它们之间不共享。
解决问题:
如何建立一种消息传输机制,使生产者和消费者之间相互不感知?
解决方案:
发布订阅模式由3个要素组成。组件通过消息或事件交互。发布者将消息发布到总线上,订阅者注册它们感兴趣的事件,订阅管理器负责将这些事件的副本发送给订阅者。
- Publisher:通过发布接口发布事件。
- Subscriber:通过订阅接口订阅事件。
- Subscription Manager:接收发布者发布的事件,并将事件发布给订阅该事件的订阅者。
模式缺点:
- 增加系统响应时延,对系统的可扩展性和消息传递时间的可预测性有负面影响。
- 难以控制消息序列。
- 难以保证消息传递的可靠性。
共享数据架构模式
适用场景:
系统中不同的计算组件需要共享和操作大量数据,这些数据不单独属于某一个组件。
解决问题:
系统如何存储和操作这些持久化数据?
解决方案:
共享数据模式由三部分组成。
- 共享数据库:协调数据访问器间的通信,功能包括数据存储、性能属性、访问控制等。
- 数据访问器:发起数据访问。只与数据库进行交互。
- 数据读写连接件:负责数据的读写。
模式缺点:
- 共享数据库可能会成为系统性能瓶颈。
- 共享数据库是单点失效节点。
- 数据的生产者和消费者的耦合度高。
黑板架构模式
适用场景:
一个新兴的或者不成熟的领域,没有确定的或优化的问题解决方案。软件系统需要集成多种形态不同的模块,以便实现复杂的、非确定性的控制策略。如语音识别、如人工智能领域等。
解决问题:
问题设计多个专业子领域,每个子领域都有一套独立的算法。鉴于领域不成熟,可能需要尝试不同的算法。因为涉及不可靠的数据,只能求近似解,无法找到最优解。算法的执行顺序不确定时还可能要求支持并行性。
解决方案:
Blackboard架构模式对还未找到确定解决策略的问题很有帮助。在Blackboard模式中,多个专业子系统通过集思广益,获得可能的部分解或近似解。
Blackboard架构背后的理念是,一系列独立的程序携手合作,在公共数据结构(即blackboard)上进行计算,中央控制器根据结果状态对知识源进行协调控制。
在解决问题的过程中,系统通过合并、修正或否决部分解来完成工作。每个部分解都针对一个子问题,是这个子问题的最终解在特定阶段的表现形式。所有的可能解构成解空间,并被组织成多个抽象层级,其中最低层为输入的内部表示,最高层包含系统要解决的整个问题的可能解。
之所以使用名称“黑板”(blackboard),是因为它让人想起专家们站在黑板前协作解决问题的情形。专家们通常自行决定接下来该由谁来到黑板前,而在这里介绍的模式中,如果有多个程序都能提供帮助,将由调停者(moderator)组件决定这些程序的执行顺序。
黑板模式包含3个要素。
- 黑板:中央数据存储区,解空间中的元素及控制数据都存储在这里。黑板提供了一个接口,让所有知识源都能够对其进行读写。
- 知识源:一个独立的子系统,解决整个问题的特定方面。
- 控制组件:运行一个监视黑板内容变化的循环,并决定接下来采取什么措施。
模式缺点:
- 无最优解。
- 并发支持程度低。
- 难以开发、测试。
事件驱动架构模式
适用场景:
异步系统。系统对进入的独立、异步事件,不管事件量多寡急缓,都需要对事件进行按需及时处理,
解决问题:
如何构建可以处理异步到达消息/事件的分布式系统,并且具有弹性的扩展能力?
解决方案:
部署独立的事件处理器用于事件处理,对到达的事件进行排队。分发器从队列中拉取事件,并基于调度策略把事件分发到合适的事件处理器进行处理。事件驱动架构(event-driven architecture)由四部分组成。
- 事件队列(event queue):接收事件的入口。
- 分发器(event mediator):将不同的事件分发到不同的业务逻辑单元。
- 事件通道(event channel):分发器与处理器之间的联系渠道。
- 事件处理器(event processor):实现业务逻辑,处理完成后会发出事件,触发下一步操作。
模式缺点:
- 涉及异步编程(要考虑远程通信、失去响应等情况),开发相对复杂
- 难以支持原子性操作,因为事件通过会涉及多个处理器,很难回滚
- 分布式和异步特性导致这个架构较难测试
MapReduce架构模式
适用场景:
对PB数量级的巨量数据进行快速分析、计算的业务场景。
解决问题:
如何高效率地对巨量数据进行分布式、并行排序,并提供一种分析、使用的简单方法。
解决方案:
MapReduce模式提供一种对分散的巨量数据的并行分析方法。这种并行方法保证低时延和高可用性。map执行数据的提取和转换,reduce对map结果进行合并。改模式分为3部分。
- Map(映射):对一些独立元素组成的列表的每一个元素进行指定的操作,可以高度并行。
- Reduce(规约):对一个列表的元素进行合并。
- Infrastructure(基础设施):负责map和reduce实例的部署,数据守护,异常监测和恢复。
模式缺点:
- MapReduce开销较大,特别是数据集较小时。
- 要求数据集能划分为相似大小的子数据集。
- 多个reduce间协调操作复杂。
参考资料
《软件架构设计:程序员向架构师转型必备》
《聊聊架构:洞见架构之道》
List of software architecture styles and patterns
Software Requirements and Architecture Engineering
(完)