表现层状态转换(英语:Representational State Transfer,缩写:REST)是Roy Thomas Fielding博士于2000年在他的博士论文中提出来的一种万维网软件架构风格,目的是便于不同软件/程序在网络(例如互联网)中互相传递信息。
讲架构风格时,需要先讲讲设计模式、架构模式的定义。
在《设计模式》这本书的「What is a Design Pattern?」小节,对设计模式下了一个明确的定义:
The design patterns in this book are descriptions of communicating objects and classes that are customized to solve a general design problem in a particular context.
设计模式描述了一组类和对象的关系,用以解决特定上下文内的某个常见的设计问题!
那我们可以这么定义架构模式:架构模式描述了一组组件之间的关系,用以解决特定上下文内的某个常见的架构问题!
维基百科上也给架构模式做了类似的定义:
An architectural pattern is a general, reusable solution to a commonly occurring problem in software architecture within a given context
架构模式是一个通用的、可重用的解决方案,用以解决特定上下文内的某个常见的架构问题!
Roy Thomas Fielding博士,在他的REST论文中,对架构风格做出了定义:
An architectural style is a coordinated set of architectural constraints that restricts the roles/features of architectural elements and the allowed relationships among those elements within any architecture that conforms to that style.
一种架构风格是一组协作的架构约束,这些约束限制了架构元素的角色和功能,以及在任何一个遵循该风格的架构中允许存在的元素之间的关系。
Martin Flower在微服务文章中的说明,也间接支持了此定义。文中首先明确「微服务」是一种架构风格,然后给出了微服务所具有的特征(就是约束),具有这些约束的系统就可以说是使用了微服务架构风格!
REST 是一种架构风格,它包含了一个分布式超文本系统中对于组件、连接器和数据的约束。REST 是作为互联网自身架构的抽象而出现的,其关键在于所定义的架构上的各种约束。只有满足这些约束,才能称之为符合 REST 架构风格。
下面介绍一下Rest架构风格的6种架构约束。
客户端-服务器架构背后的约束原则是分离关注点。通过将用户界面功能(User interface functionality)移入到C端,服务器端只做数据存储, 简化了服务器组件,提高了可伸缩性。只要通信接口不发生变化,C端跟S端都能独立的演进。
无状态要求通信本质上是无状态的。无状态要求从客户端发到服务器的所有请求都必须包含理解该请求的全部信息,不能利用任何存储在服务器端的上下文,会话状态因此要全部保存在客户端。在不同的客户端请求之间,服务器并不保存客户端相关的上下文状态信息。
无状态给架构带来了三方面的好处:可见性,可靠性,可伸缩性。
架构设计本身就是一种权衡,无状态虽然带来了上述优点。它也带来了一些不利的影响
缓存可以改变网络的效率。要求一个请求的响应中的数据被隐式的或者显示的标记为可缓存或者不可缓存的。如果是可缓存的,客户端缓存就可以为以后的相同请求重用这个响应的数据。
缓存架构约束的好处在于它有可能部分或者全部消除一些交互,从而减少系列交互的平均延迟时间,来提高效率、可伸缩性和用户感知的性能。这样也有代价,如果缓存中陈旧的数据与请求直接发送到服务器得到的数据差别会很大,那么缓存会降低可靠性。
统一接口是REST架构风格的核心特征,它强调组件间要有一个统一的接口。
通过在组件接口上应用通用性的软件工程原则,简化了整体的系统架构,也改善了交互的可见性。实现与他们提供的服务是解耦的,这促进了独立的演进性。当然,统一接口也会影响效率,因为信息都使用标准化的形式来提交,而不能使用特定于应用的需求的形式。
REST接口被设计为可以高效的传输大粒度的媒体数据,并针对Web的常见情况作了优化,但这也是导致该接口对于其他形式的架构交互而言不是最优的。
那么怎么样才能有统一的接口呢?REST由四个接口架构约束来定义:
统一接口的请求的参数有请求的控制数据、一个(表示请求的目标的)资源标识符、以及一个可选的表述组成。输出参数有响应的控制数据、可选的资源元数据、以及一个可选的表述组成。统一接口这部分东西很多,在后面专门有一节讨论一下。
一个分层系统是按照层次来组织的,没一层为在其之上的层提供服务,并且使用在其之下的层所提供的服务。并且内部层对于相邻外部层之外的所有层而言,是被隐藏起来的。通过这样做,减少了跨越多层的耦合,从而改善了可进化性和可重用性。分层的系统的例子很多,最知名的就是TCP/IP的四层模型和OSI的七层模型。
在分层的系统中,可以有中间件来支持跨多个网络和处理器的负载均衡,处理安全策略和缓存等相关问题,以提高系统的可伸缩性。客户端、服务器并不需要了解中间的这些层次的细节。中间组件还能够主动的转换消息的内容,因为这些消息是自描述的,并且其语义对于中间件是可见的。
分层系统的主要缺点是:增加了数据处理的开销和延迟,因此降低了用户感知的性能。当然,这个问题可以通过在中间层增加缓存来弥补这个问题。
用户感知的性能,是根据对用户的影响来度量的,主要度量手段是延迟(latency)和完成时间(completion time)。
延迟(latency)是指从最初的触发请求到最早的响应指示之间持续的时间。在网络应用中,主要会出现在这些点上:1)应用对于触发动作的事件的响应时间。2)在组件之间建立交互所需的时间。 3)将交互请求数据传输到每个组件所需要的时间。4)在每个组件上处理每个交互请求所需的时间;5)应用通过移交和处理交互结果,最后呈现结果到用户面前的时间。
完成时间(completion)是完成一个应用动作花费的时间。完成时间与延迟的区别在于,延迟代表一个应用能够增量的处理正在接收的数据的程度。延迟可以认为是请求到响应最短时间,从用户发送请求,到收到第一个交互响应为止。而完成时间应该是用户请求最终完成的时间。这个从下载文件可以比较容易看出来,点击下载,到显示1%的下载进度的时间为100ms,则延时为100ms。而完成时间需要等待整个文件下载完成。
延迟的优化往往延长完成时间,反之亦然。比如通过压缩传输的数据传输,可以减少完成时间,但是延迟可能更高,因为组件处理增加了数据压缩的时间。所以在优化时,需要关注接收者更关注的是延时(例如Web浏览器)还是完成时间(Web爬虫)。
服务器可以通过传输可执行代码的方式来扩展或自定义客户端的行为,对客户端进行扩展。这样可以减少被预先实现的功能的数量,简化客户端开发。而且允许在部署之后下载功能代码,也能改善系统的可扩展性。这是一个可选的约束。
可扩展性就是将功能添加到一个系统中的能力。动态可扩展性就是将功能添加到已经部署的系统中,而不影响系统的其他部分。