衔接上文[解读REST] 3.基于网络应用的架构,上文介绍了一组自洽的术语来描述和解释软件架构;如何利用架构属性评估一个架构风格;以及对于基于网络的应用架构来说,那些架构属性是值得我们重点关注评估的。本篇在以上的基础上,列举一下一些常见的(REST除外)的适用于基于网络应用的架构风格,并使用对比架构属性的方式对其进行评估。
架构设计的目的是为了满足或者超出应用的需求,而不是为了创造出一种特殊的交互拓扑或者一种特殊的设计方式。当设计一个系统时所选择的架构风格,必须与这些需求保持一致,而不是相抵触。因此应该依据这些架构风格所产生的架构属性来对架构风格进行评估。架构属性是相对的,如果添加以一个架构约束,增强了某一个架构属性,也可能会消弱另外一个架构属性;此外,一个架构属性是被增强了还是被消弱了,也会受到系统实现的的影响。
下面几个小节来评估每一种架构风格,以及它们都会产生那些架构属性。(+)表示增强改善,(-)表示消弱,(±)表示取决于具体的场景。
在PF风格中,每个组件(过滤器)会从输入端读取数据,并在输出端输出数据(亦可以增量处理,而不必等到全部处理完再交给下一个过滤器)。这里的架构约束是一个过滤器必须完全独立于其他的过滤器(零耦合)。多个过滤器这样头尾相连组合起来,就像是一个管道,所以称之为管道和过滤器风格(简称PF)。这种风格可以产生如下几个的架构属性:
具体的例子:比如linux shell,asp.net core中的middleware和filter等。
UPF在PF的基础上,增加了一个所有过滤器都必须具有相同接口的约束。这个约束会在PF的基础上,产生如下的架构属性:
具体的例子:比如linux的标准输入输出流,asp.net core中的middleware也算是(没有跨越网络)具有相同接口的约束的UPF。
通过利用多个进程来提供相同的服务,即为RR风格。这些多个分散的服务对于客户端来说,就好像是只有一个集中的服务。这种风格可以产生如下几个的架构属性:
具体的例子:比如GIT(分布式的版本管理系统本地有着完整的仓库副本),CVS(集中式的版本管理系统本地也是有完整的仓库副本的,只是写操作是需要网络的)等。
缓存风格是复制仓库风格的一个变种:复制个别请求的结果,以便被后面的请求复用这些结果。这种风格可以产生如下几个的架构属性:
具体的例子:DNS缓存,CDN等。
服务器组件提供了一组服务,并监听对这些服务的请求;客户端组件通过一个连接器把请求发送给服务器。服务器可以拒绝这个请求,也可以执行这个请求,并把响应发送给客户端。服务端通常来说是一个永不终止的进程,通常是为多个客户端提供服务的。这个约束背后的原则是分离关注点(但是并不关心会话状态是在服务端还是客户端),这种风格可以产生如下几个的架构属性:
具体的例子:RPC等。
按照层次来组织,每一层都使用下层的服务,并且为其上层提供服务。层内部的细节对于相邻的层而已是完全被隐藏起来的。这种风格可以产生如下几个的架构属性:
具体的例子:TCP/IP协议,分层的网络协议栈。
LCS是在LS和CS的结合体。可以理解为在CS的基础上增加了代理组件和网关组件,对于客户端而言,代理组件是一个共享服务器,它接受请求并把它转发给服务器。网关组件在客户端组件和代理组件看来就像是一个正常的服务器,只是网关组件内部只是将它转发给了内部的其他服务器。在CS和LS的基础上,LCS改善了如下的架构属性:
在CS的基础上,增加一个约束:服务端组件上不允许有会话状态。从客户端发给的服务器的每个请求都必须包含理解请求所必须的所有信息,即不能在服务端上保存请求上下文信息(比如上一个请求的信息),会话状态应该都保存的客户端。这个约束会在CS的基础上,产生如下的架构属性:
具体的例子:大多数桌面(或者APP)应用。
在CSS的基础上,增加缓存组件。缓存在客户端和服务器直接进行周旋:它可以复用早先的请求,用来响应后面的相同请求,从而避免向原始服务器发送请求(得到的响应是一样的)。增加的这个组件可以在CSS的基础上进一步改善以下的架构属性:
具体的例子:SUN的NFS。
合并了LCS和C$SS两种风格,其产生的架构属性为LCS和C$SS的组合(但是不会把重复的CS结算两次)。
具体的例子:DNS系统。
RS是CS的一种变体,它试图使客户端组件的复杂性最小化或者使它们的可重用性最大化。每个客户在服务器上启动一个会话,然后调用服务器的一系列服务,最后退出会话。应用状态被保存在服务器上。这种风格可以产生如下几个的架构属性:
具体的例子:Telnet,SSH。
RDA是CS的一种变体,它把应用状态分布在客户端和服务端上。客户端发送一个标准的数据查询请求给服务端,服务端分配一个工作空间并执行这个查询,这可能会产生一个巨大的结果集。客户端可以在在结果集上进行进一步操作。这就需要客户端了解服务端的数据结构,以便构造依赖该结构的查询。这种风格可以产生如下几个的架构属性:
具体的例子:SQL。
所有的移动代码风格的基础都是VM(或解释器)风格。VM风格本身并不是基于网络的风格,但是它通常在REV和COD风格中于一个组件结合在一起使用。代码在一个满足了安全和可靠性的受控的环境中执行,VM通常被用作脚本语言的引擎,来执行特定的任务。这种风格可以产生如下几个的架构属性:
REV风格来源于CS+VM风格,客户端组件必须知道如何执行一个服务,但是缺少执行此服务所必须的资源,而这些资源都位于一个服务端站点上。因此,客户端组件把如何执行服务的代码发送给服务端的一个服务端组件,由它来执行代码,然后把结果返回给客户端。这种REV会要求被执行的代码是在一个受保护的环境中,使其不会影响到其他的客户端。这个约束在CS+VM的基础上可以产生如下的架构属性:
具体的例子:redis可以执行lua脚本。
COM风格来源于CS+VM风格(但是又不同于REV),客户端组件知道如何访问一组资源,但是不知道如何处理它们。客户端需要向服务端请求一份可以处理这部分资源的代码,这部分代码在客户端本地执行。这个约束在CS+VM的基础上可以产生如下的架构属性:
具体的例子:浏览器中的JS。
把COD添加到签名所说的LC$SS风格上,这时候把代码被看作是另一种形式的数据元素,因此并不会妨碍LC$SS的优点,同时也会叠加COD的优点。
MA风格来源于REV+COD。在MA中,一个完整的计算组件,它的状态,代码、执行代码所需的数据都被一起移动到了远程站点。它是已REV和COM两种方式同时工作的。
基于事件集成的风格也被成为隐式调用风格或者事件系统风格。通过消除了解连接器接口的标识信息的必要性,它可以降低组件之间的耦合。此架构风格不是之间调用另外一个组件,而是通过一个组件发布或者广播一个或多个事件。然后由系统负责调用其他注册了对这些事件感兴趣的组件。这样的系统一般都会有一个事件总线,所有的组件都通过这个总线监听它们各自感兴趣的事件。这种风格可以产生如下几个的架构属性:
具体的例子:发布/订阅的消息系统。
C2是EBI和LCS的组合形成的风格。C2在EBI的基础上,支持大粒度的重用,并通过加强基础层独立性来支持系统组件的灵活组合。异步通知消息向下传递,异步请求消息向上传递,这是组件之间通信的唯一方式。这个约束加强了对高层依赖的松耦合(服务请求可以被忽略),并且于底层实现了零耦合(无需知道系统使用了通知),这样既改善了对整个系统的控制,又没有丧失EBI的大多数优点。
通知是对于组件中的状态变化的公告,C2并不会对通知中应该包含什么内容加以限制。连接器的首要职责是消息的路由和广播,其次是消息的过滤。引入对于消息的分层过滤,可以解决EBI的可伸缩性的问题,同时改善可进化性和可重用性。
DO是CS和CS的组合。在单独的CS的风格总,客户端和服务端是相互独立的,各自只负责自己的部分。DO则是使一个组件既是客户端也是服务端。也就是说它既对外提供服务,同时也是服务的消费方。DO组件的内部是完全被隐藏和保护起来的,操作它的唯一办法是通过它公开的接口进行访问。一个DO为了要和另外一个DO交互,则必须知道另外一个DO的标识信息,当一个DO的标识信息发生变化的时候,则必须要修改所有显示调用它的DO。因此必须要又一些控制器对象来负责管理维护系统的状态。
为了降低DO中受到对象标识信息的影响,通常会使用一种或者多种架构风格来辅助通信,比如EBI和被代理的CS风格。这样的目的在于引入一个名称解析组件,用来把一个通用的服务的名称解析为一个能够满足该请求的对象的特定名称,并使用这个特定名称的对象来处理请求。尽管它改善了可重用性和可进化性,但是额外的间接层会产生一定的网络开销,从而降低用户感知的性能。具体的例子:CORBA,ODP。
以上的每一种架构风格都在组件之间推崇一种特定的交互类型。当组件跨域广域网的分布的时候,应用的可以用就会取决于对于网络的使用或者误用。通过对已架构风格对于架构属性的影响来刻画架构,才能选择出更适合此类应用的架构设计。
但是以上的评估是有一些局限性的,这里的评估是特别为分布式超媒体系统的需求量身定做的。比如通信的内容是细粒度的控制信息,那么PF风格的很多优点就不复存在了;而且如果用户交互的通信如果是必须的,PF则根本就不适用。同样的,如果客户端没有对请求进行缓存,那么分层+缓存的风格则只会增加延迟,而不会带来任何好处。这样的问题需要针对每一种类型的通信问题进行单独对比。下面的表格总结一下上面介绍到的所有的架构风格。
风格 | 继承 | 网络性能 | 用户感知的性能 | 网络效率 | 可伸缩性 | 简单性 | 可进化性 | 可扩展性 | 可定制性 | 可配置性 | 可重用性 | 可见性 | 可移植性 | 可靠性 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
PF | ± | + | + | + | + | + | ||||||||
UPF | PF | - | ± | ++ | + | + | ++ | ++ | + | |||||
RR | ++ | + | + | |||||||||||
$ | RR | + | + | + | + | |||||||||
CS | + | + | + | |||||||||||
LS | - | + | + | + | + | |||||||||
LCS | CS+LS | - | ++ | + | ++ | + | + | |||||||
CSS | CS | - | ++ | + | + | + | + | |||||||
C$SS | CSS+$ | - | + | + | ++ | + | + | + | + | |||||
LC$SS | LCS+C$SS | - | ± | + | +++ | ++ | ++ | + | + | + | + | |||
RS | CS | + | - | + | + | - | ||||||||
RDA | CS | + | - | - | + | - | ||||||||
VM | ± | + | - | + | ||||||||||
REV | CS+VM | + | - | ± | + | + | - | + | - | |||||
COD | CS+VM | + | + | + | ± | + | + | - | ||||||
LCODC$SS | LC$SS+ COD | - | ++ | ++ | ++ | +±+ | ++ | + | + | + | ± | + | + | |
MA | REV+COD | + | ++ | ± | ++ | + | + | - | + | |||||
EBI | + | -- | ± | + | + | + | + | - | - | |||||
C2 | EBI+LCS | - | + | + | ++ | + | + | ++ | ± | + | ± | |||
DO | CS+CS | - | + | + | + | + | + | - | - | |||||
BDO | DO+LCS | - | - | ++ | + | + | ++ | - | + |
做了4篇博客的前期准备工作,下一篇就开始介绍什么是REST了。以上 如有错误之处,欢迎指正!
理解本真的REST:http://www.infoq.com/cn/articles/understanding-restful-style/
架构风格与基于网络的软件架构设计-导读:http://www.infoq.com/cn/articles/doctor-fielding-article-review
架构风格与基于网络的软件架构设计:http://www.infoq.com/cn/minibooks/web-based-apps-archit-design
Architectural Styles and the Design of Network-based Software Architectures:https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm