SyMind / learning

路漫漫其修远兮,吾将上下而求索。
9 stars 1 forks source link

架构风格与基于网络的软件架构设计 #52

Open SyMind opened 1 year ago

SyMind commented 1 year ago

论文摘要

软件架构的研究探索了如何以最佳的方式划分一个系统、如何标识组件、组件之间如何通信、信息如何沟通、系统的元素如何能够独立地进化,以及上述的所有东西如何能够使用形式化的和非形式化的符号加以描述。

表述性状态转移(REST)

推导 REST

从『空』风格开始

『空』风格仅仅是一个空的约束集合。从架构的观点来看,空风格描述了一个组件之间没有明显边界的系统。这就是我们描述 REST 的起点。

客户-服务器

首先被添加到我们的混合风格中的约束是客户-服务器架构风格。客户-服务器约束背后的原则是关注点分离。通过分离用户接口和数据存储这两个关注点,我们改善了用户接口跨多个平台的可移植性;同时通过简化服务器组件,改善了系统的可伸缩性。然而,对于 Web 来说,最重要的是这种关注点分离允许组件独立地进化,从而支持多个组织领域的 Internet 规模的需求。

无状态

我们接下来再为客户-服务器交互添加一个约束,形成客户-无状态-服务器:通信必须在本质上是无状态的,因此从客户到服务器的每个请求都必须包含理解该请求所必须的所有信息,不能利用任何存储在服务器上的上下文,会话状态因此要全部保存在客户端。

缓存

为了改善网络的效率,我们添加了缓存约束,从而形成客户-缓存-无状态-服务器风格。缓存约束要求一个请求的响应中的数据被隐式地或显式地标记为可缓存的或不可缓存的。如果响应是可缓存的,那么客户端缓存就可以为以后的相同请求重用这个响应数据。

统一接口

使 REST 架构风格区别于其他基于网络的架构风格的核心特征是,它强调组件之间要有一个统一的接口。通过在组件接口上应用通用性的软件工程原则,整体的系统架构得到了简化,交互的可见性也得到了改善。实现与它们所提供的服务是解耦的,这促进了独立的可进化性。然而,付出的代价是,统一接口降低了效率,因为信息都使用标准化的形式来转移,而不能使用特定于应用的需求的形式。REST 接口被设计为可以高效地转移大粒度的超媒体数据,并针对 Web 的常见情况做了优化,但是这也导致了该接口对于其他形式的架构交互并不是最优的。

为了获得统一的接口,需要有多个架构约束来指导组件的行为。REST 由四个接口约束来定义:资源的识别、通过表述对资源执行的操作、自描述的消息、以及作为应用状态引擎的超媒体。

REST 架构的元素

数据元素(Data Elements)

连接器(Connectors)

组件(Components)

REST 架构的视图

经验与评估

Web 标准化

开发 REST 的动机是为 Web 应该如何运转创建一种架构模型,使之称为 Web 协议标准的指导框架。

将 REST 应用于 URI

REST 被用来为 URI 标准定义术语『资源』,也被用来定义通过它们的表述来操作资源的通用接口的全部语义。

在 REST 中对于『资源』的定义基于一个简单的前提:标识符的改变应该你可能少地发生。

将 REST 应用于 HTTP

性能

架构上的教训

基于网络的 API 的优势

HTTP 并不是 RPC

HTTP 并不是一种传输协议

HTTP 并不是被设计为一种传输协议(transport protocol),它是一种转移协议(transfer protocol)。在 HTTP 协议中,消息通过在那些资源的表述上的转移和操作,来对资源执行一些动作,从而反映出 Web 架构的语义。

媒体类型的设计

结论

REST 是一组协作的架构约束,它试图使延迟和网络通信最小化,同时使组件实现的独立性和可伸缩性最大化。REST 通过将约束放置在连接器的语义上来达到这些目标,而其他的架构风格则聚焦于组件的语义。REST 支持交互的缓存和重用、动态替换组件、以及中间组件对于动作的处理,因此满足一个 Internet 规模的分布式超媒体系统的需求。

缺点

RESTful API 基于引用资源的 URI 路径。比如,『返回3个小时内所有更新过的记录,且其中包含 cat 单词』这样的请求不适合用 URI 路径来表示,因此很可能需要使用 URI 路径、查询字符串和请求体的某种组合来实现。

RESTful API 依赖于一些 http 方法(GET、POST、PUT、DELETE,也许还有 PATCH),但许多常见的客户端/服务器操作只能尴尬地被塞进一个标准方法中。『将过期文件移动到归档文件夹』就是一个应用程序的自定义谓词,它不属于典型的 http 方法和 CRUD 操作。

对 RESTful API 的依赖是 HTTP 和相关互联网基础建设成功的不幸结果。一种更通用的请求和响应方法,例如使用JSON-RPC,允许更具表现力、更少麻烦的API设计。

REST 资源命名指南

单一资源与集合资源

资源可以是单一的也可以是一个集合。

例如,『customers』是集合资源,『customer』是单一资源。

我们可以使用 URI『/customers』指定集合资源『customers』,使用URI『/customers/{customerId}』标识单一资源。

集合资源与子集资源

一个资源能包含子集资源。

例如,一个『customer』的子集资源『accounts』可以使用 URI『/customers/{customerId}/accounts』来标识。

同样,子集『accounts』下的一个单一资源『account』可以使用『/customers/{customerId}/accounts/{accountId}』来标识。

最佳实践

使用名词来表示资源

document

集合

store

controller

controller 资源为过程概念建模。controller 资源像执行函数一样,拥有参数和返回值、输入和输出。

使用『动词』标识 controller。

http://api.example.com/cart-management/users/{id}/cart/checkout http://api.example.com/song-management/users/{id}/playlist/play

一致性是关键

使用正斜杠(/)表示层级关系

http://api.example.com/device-management
http://api.example.com/device-management/managed-devices
http://api.example.com/device-management/managed-devices/{id}
http://api.example.com/device-management/managed-devices/{id}/scripts
http://api.example.com/device-management/managed-devices/{id}/scripts/{id}

不要在 URI 尾部使用正斜杠(/)

http://api.example.com/device-management/managed-devices/ http://api.example.com/device-management/managed-devices  /*This is much better version*/

使用连字符(-)提高 URI 的可读性

http://api.example.com/device-management/managed-devices/
http://api.example.com/device-management/managed-devices    /*This is much better version*/

不要使用下划线(_)

http://api.example.com/inventory-management/managed-entities/{id}/install-script-location  //More readable

http://api.example.com/inventory-management/managedEntities/{id}/installScriptLocation  //Less readable

在 URI 中使用小写字母

不要使用文件后缀

文件扩展名很糟糕,没有增加任何优势。删除它们也会减少 URI 的长度,没有理由保留它们。

除了上述原因之外,如果你想使用文件扩展名突出显示 API 的媒体类型,那么你应该依赖 Content-type 头的媒体类型来确定如何处理主体的内容。

不要在 URI 中使用 CURD 方法名

HTTP GET http://api.example.com/device-management/managed-devices  //Get all devices
HTTP POST http://api.example.com/device-management/managed-devices  //Create new Device

HTTP GET http://api.example.com/device-management/managed-devices/{id}  //Get device for given Id
HTTP PUT http://api.example.com/device-management/managed-devices/{id}  //Update device for given Id
HTTP DELETE http://api.example.com/device-management/managed-devices/{id}  //Delete device for given Id

使用查询参数来筛选 URI 集合

http://api.example.com/device-management/managed-devices
http://api.example.com/device-management/managed-devices?region=USA
http://api.example.com/device-management/managed-devices?region=USA&brand=XYZ
http://api.example.com/device-management/managed-devices?region=USA&brand=XYZ&sort=installation-date

参考资料

  1. https://www.academia.edu/34136202/Fielding%E5%8D%9A%E5%A3%AB%E7%9A%84REST%E8%AE%BA%E6%96%87%E4%B8%AD%E6%96%87%E7%89%88
  2. https://restfulapi.net/resource-naming/