深入解析REST架构风格

社区编辑2018-05-15 18:35

REST API已经被广泛应用在各种框架中,尤其是当前热门的微服务,更是热衷于REST这样的轻量级协议然而,当前的技术圈子,即便是很多程序员,对REST也未能透彻理解。本文为大家清晰推导出REST的内涵诠释我们软件架构中应当如何使用

 

一说到REST,估计很多人的第一印象就是RESTful的URL,URL本身只指向一个特定的资源,对该资源的具体操作不体现在URL中,而是由具体的HTTP请求方法来表示对资源的具体操作,GET是查询,PUT是更新,POST是创建,DELETE是删除等等。这应该就是大家平常工作中所能用到的全部REST相关的知识吧。但是,REST真的是只有这样吗?RESTful的架构风格是怎么样的?

 

首先我们来说一说REST(Representational State Transfer)表述性状态转移是什么意思?顾名思义,REST的关键有二:表述和状态转移。所谓表述指的是资源的表述,资源是应用中的具体实体或某种信息,我们可以简单的认为服务器是由各种离散资源组成的。资源的表述指的是资源在架构组件中的某种表现形式,可以是XML、JSON等形式。状态转移指的是某个资源在组件中状态的变化,而状态的变化是由客户端通过请求对资源执行的具体操作导致的。REST作为WEB应用的架构之一,其资源是通过URI来定位的,而对资源的操作则是通过具体的HTTP动词来指定的。

 

在了解REST的字面意思之后,我们想,这种架构风格由什么组成呢,这种架构风格带来的好处又有哪些呢?我们都知道REST架构风格主要用于指导WEB应用的架构设计。而架构设计的目标是创造一个架构属性集合,这些架构属性满足全部系统需求。架构属性我们可以认为是由架构约束导出的一组架构特征。那么WEB应用通常需要包含哪些架构属性呢,这些属性是由哪些架构约束导出的呢?在进行软件架构设计时,我们可以通过逐步将架构约束加入其中直到约束导出的架构属性满足系统的需求为止,或者是,在了解了整体系统需求,然后根据需求所需要的特定架构属性,将不同的架构约束加入其中。下文我们将根据第二种方法推导出REST风格的组成。

 

在介绍REST风格的架构包含哪几种架构约束前,我们先介绍一下常见的架构属性有哪几种。常见的架构属性有以下几种:

1.性能。性能应该是架构设计师最关心的架构属性了,其又可以具体分为网络性能、用户可感知性能和网络的效率。应用的性能主要取决于应用的需求和所选择的交互风格。

2.可伸缩性。可伸缩性代表了架构组件之间的交互能力的强弱。可伸缩性可以通过简化组件、分散交互等手段来提高。另外交互风格的设置也对系统的伸缩性有重要影响,例如当应用组件之间采用RabbitMQ为消息中间件进行交互时,那么消息送达的at-least-once和at-most-once两种语义的选择就将严重影响系统的可伸缩性。

3.简单性。简单性我们可以理解为架构组件功能划分的合理性。组件功能划分越单一,提供的服务越单一,简单性就越好,简单性可以通过应用通用性原则来增强。

4.可修改性。可修改性指的是架构组件能够被改动的容易程度。这种容易程度的度量是建立在组件的改动不影响应用的整体性能,不影响组件改动前所能提供的服务,不影响其他组件所能提供的服务上的。可修改性具体又由可扩展性、可进化性、可定制性等组成。

5.可见性。可见性指的是一个架构组件对于架构中其他组件之间交互的可感知的能力。

6.可靠性。可靠性指的是架构受到故障时对系统服务的影响程度。这里的故障并非一定是机器故障或者人为事故,也可能是缓存失效等与程序预期不符的场景。架构组件在处理以上场景时,如果对系统服务的影响越小,则我们认为其越可靠。

 

在了解了常见的几种架构属性之后,回到最初的问题,REST格包含哪些属性呢,这些属性是由哪些架构约束导致的呢,为什么这些架构约束会被加入到其中呢?


图1 客户端-服务器架构模式[1]

首先,REST架构风格被设计用来指导web应用的架构设计,web应用通常是一个CS(client-server)架构的应用,客户端向服务端发送请求,服务端处理请求并返回请求结果,如图1所示。因此第一个加入到REST风格的架构约束是CS架构。由于客户端实现和服务实现的分离,该约束将提高应用的简单性和可扩展性。


图2 CS-无状态架构模式[1]

第二个加入到REST风格的约束是无状态,REST风格的web应用应该是一个无状态的应用,如图2所示。为什么呢?我们都知道REST是表述性状态转移,在一个REST风格的应用中,我们可以认为一切皆资源,所有资源都应该可以通过特定的URI唯一定位到,而实现该目的的主要方法就是保证服务是无状态的。因为有状态服务会保存请求的上下文信息,其并不能保证每次对同一资源进行相同操作时都得到相同的结果。比如在一个REST风格的员工管理系统当中,员工是一种资源,有URI唯一定位到某个员工,那么每次发送HTTP GET请求都能准确获取到该员工的信息。如果应用是有状态的,则对员工信息的查询请求可能会受到上一次请求的影响。无状态约束条件下,由于服务端不保存请求上下文信息,无需因此而占用过多的服务器资源而提高应用的可伸缩性、由于请求之间相互独立,应用更容易从局部故障中恢复而提高应用的可靠性、由于单次请求包含了所有该请求所需要的信息而提高了应用的可见性。


图3 CS-无状态-缓存架构模式[1]

第三个加入到REST风格就是缓存约束,如图3所示,图中的$符号代表Cache。由于REST风格是一种无状态的架构风格,每次请求都需要带上理解该次请求所需的全部信息,此举势必导致应用的网络效率的降低,并导致系统组件之间的交互次数的增加。为此,REST风格加入了缓存约束。缓存约束的加入能够部分或全部减少客户端和服务端的交互,从而提高应用的网络效率和用户可感知的性能,应用的可伸缩性也因此提高。但是该约束的加入也可能因为缓存的失效而影响应用的可靠性。

 

到此应该是大家最为熟悉的,也是一般WEB应用最常用的架构模式了,那这就是REST了吗?显然不是,RPC架构风格的应用也可以是以上架构。REST与前者最大的不同,也可能是大家最熟悉的REST架构约束——统一接口约束。该约束强调的是架构组件之间应该有统一的接口规范,要求不同的组件对资源进行操作时必须通过统一接口来实现,主要包括了资源描述的统一和资源操作的统一。接口规范的统一使得在接口不变的前提下,可以修改接口的具体实现,从而提高了系统可修改性。各个架构组件之间统一的接口规范也使得各组件理解接口含义变得简单,从而提高系统的可见性。

 

为了进一步提高应用的可伸缩性和可扩展性,REST风格还加入了分层系统约束。系统分层要求应用的组件之间有明确的界限,各层各司其职,每一层只和相邻层有交互,其他非相邻的层并不知道该层的存在,这就减少了跨层之间的耦合度,改善了架构组件的可进化性和可重用性。但是由于数据的处理可能需要在系统的多层中进行,因此分层系统会增加数据处理的开销,进而影响用户可感知的性能。

 

最后一个加入到REST风格的约束是按需代码,这是一个可选约束。按需代码约束的加入允许应用在需要的时候加入新的功能组件,因此提高了系统的可修改性(可定制性、可扩展性和可进化性)。但是也由于老的组件对新组件的不了解而降低了架构组件之间的可见性。因此该约束是REST风格的一个可选约束。

 

综上可知,一个REST风格的架构应该包含一下几个特征:CS、无状态、缓存、统一接口和分层,在进行web应用架构时,如果需要实现一个REST风格的应用,则该应用应该具备以上几个特征。以上就是本次咬文嚼字的全部内容,大体介绍了REST架构风格的组成和其组成的来源(动机)。

 

写在最后的话:在实际的工作中,如果我们要以REST风格为指导来进行软件架构时,还是应该从应用的具体需求出发,而不一定非得将以上五种(和一种备选)架构约束加入到我们的架构当中。例如:当应用规模还很小时,不必非得将缓存约束加入到其中以缓解客户端与服务端的交互压力,加入缓存约束后,架构还需要考虑缓存的可靠性,缓存的失效时间和缓存的更新等一些列问题,得不偿失。当然,在REST风格的架构约束所能导出的架构属性不能满足某些应用需求时(如安全需求),我们还应该将能够导出所需属性的架构约束加入到我们的架构当中来。总之,灵活运用,毕竟,合适的才是最好的。

 

参考文献:

[1] Fielding R T. Architectural styles and the design of network-based software architectures[D]. University of California, Irvine, 2000.

[2] 李锟, 廖志刚, 刘丹, 等. 架构风格与基于网络的软件架构设计[D]. , 2000.

[3] http://www.infoq.com/cn/articles/understanding-restful-style

[4] http://blog.jobbole.com/41233/

[5] http://www.infoq.com/cn/articles/rest-introduction

[6] https://www.zhihu.com/question/28557115