vlambda博客
学习文章列表

读书笔记《cloud-native-applications-in-java》API设计最佳实践

第 11 章 API 设计最佳实践

本章讨论如何设计以消费者为中心、细化且面向功能的 API。它还讨论了 API 设计问题的各种最佳实践,例如如何识别将用于形成 API 的资源、如何对 API 进行分类、API 错误处理、API 版本控制等。我们将介绍通过 Open API 和 RAML 描述 API 的模型。

我们将涵盖以下主题:

  • API 设计问题
  • API网关部署

API 设计问题


API 旨在被使用并且定义如何使用 API。 API 指定命令/操作列表以及与 API 交互所需的命令的格式/模式。

在定义 REST API 时,信息的关键抽象是资源。资源被定义为到一组实体的概念映射。 API 设计以构成设计核心的资源为中心。 统一资源标识符 (URI)、操作(使用HTTP方法)和 resource 表示(JSON 模式)在构建时牢记资源。对资源进行正确的抽象以实现 API 的消费、可重用性和可维护性变得非常重要。

资源可以指向单个实体或实体集合。例如,产品是单一资源,而产品是资源的集合。我们将涵盖两个级别的设计指南:

  • 如何确定正确的资源粒度级别
  • 如何围绕已识别资源设计 API

API资源识别

API 的设计与问题域的底层业务域model 相关联。 API 需要以消费者为中心,并关注消费者的需求。应用领域驱动的设计原则来确定正确的粒度。有界上下文模式是有助于将问题区域划分为不同的有界上下文明确它们之间的关系的中心模式。对于企业而言,资源识别也是由中央/集团架构团队定义的规范模型驱动的。

此外,根据 API 的定义位置和公开的特性/功能,API 可以分为三大类:

读书笔记《cloud-native-applications-in-java》API设计最佳实践

让我们在以下部分详细讨论这些类别。

系统 API

关键企业资源或系统记录需要打开或公开为一组API,供所有下游系统构建逻辑/体验这些服务。对于绿地项目,系统 API 通常表示作为功能的一部分而开发的记录系统或数据存储系统。对于企业来说,系统API代表了所有的企业系统,比如核心企业资源规划< strong>ERP)系统、运营数据存储、大型机应用程序或许多 商业现成COTS) 产品,例如 c客户关系管理 (CRM)等运行核心的 企业的流程。 ofsystem API 如下:

  • 域驱动设计的起源源于查看核心系统域,并创建有界上下文来定义系统 API。
  • 这些记录系统通常映射到 HTTP 资源类型(名词)并提供实体服务。例如,在银行账户的情况下,抵押贷款、证券和卡是构建系统 API 的核心实体或名词。
  • 有界上下文模型定义服务拥有它们的数据存储。但在现有系统的情况下,如 企业资源规划 (ERP ),服务可能 共享相同的底层系统。这需要仔细研究底层业务流程,识别领域(又名名词),并将它们作为系统 API 公开。帐户可以是系统 API,但帐户转移将是一个过程 API,它利用底层帐户系统 API 来提供服务。
  • 系统 API 传统上非常稳定,并且与通道或流程 API 层的变化无关。这些构成了核心的、稳定的企业方面的一部分。
  • 该企业系统的组成和集成机制定义了系统 API 如何与底层系统集成。例如,大型机规定使用 MQ 作为集成机制,让系统 API 实现 MQ 以将大型机功能作为 API 公开。
  • 系统 API 的最大问题是它们的正常运行时间和弹性与底层系统的稳定性有关。如果核心应用程序经常宕机或出现问题,这些问题往往会传递到系统 API 层。

流程 API

纯粹主义者会说系统 API 公开了 systems 的核心功能,应用程序应该将系统 API 的功能混搭起来为最终客户提供必要的功能。这可能适用于较小的应用程序或应用程序的初始迭代。随着应用程序变得越来越大,或者您开始​​跨多个渠道或设备公开功能,您开始查看功能开始被复制的场景,这意味着缺乏重用导致更难维护系统。流程 API 的一些显着特征如下:

  • 流程 API 提供了建立在系统 API 之上的更丰富的功能。例如,我们可以将帐户转移编写为一个流程 API,在各个渠道中重复使用,以提供一致且可重用的模型,而不是每个通道都编写帐户转移功能。
  • 从消费者的角度来看,流程 API 为访问功能提供了一个比尝试编排多个系统 API 更简单的模型。这有助于提高客户端的易用性,并有助于减少 API 网关级别的流量。
  • 流程 API 还可用于为应用程序提供跨渠道/全渠道能力。可以在此级别处理诸如通道上下文切换之类的问题。
  • 应用程序倾向于引入流程 API 来提高整个系统的性能。如果系统 API 绑定到速度较慢或只能处理有限吞吐量的系统,则可以使用进程 API 来缓存来自系统 API 的数据,以避免每次都进入底层系统。在记录系统和随后的系统 API 不可用的情况下,流程 API 可用于通过提供替代功能流来处理此类请求。
  • 流程 API 还可以充当外部第三方调用的适配器,而不是应用程序直接进行第三方调用。使用流程 API 可以让我们处理第三方 API 的故障不会影响应用程序其余部分的情况。流程 API 可以应用模式,例如断路器和限制传出请求来处理多个场景。

频道 API

最后的 API 分类是通道 API。正如 name 所暗示的,这些 API 是特定于渠道的,并映射到作为应用程序的一部分构建的客户旅程.这些也称为体验 API 或旅程 API。例如,如果您使用 Angular 或 React 构建应用程序,则属于 单页应用程序 (SPA) 需要映射到可由通道 API 提供的底层服务。通道 API 的一些显着特征如下:

  • 渠道 API 映射到客户旅程,这些旅程总是与渠道相关联。这些有时也称为体验 API。这些 API 可以是有状态的,因为它们在客户旅程中为客户服务并且需要携带会话上下文。可以通过将状态外部化到 Redis 等会话存储来构建无状态服务。
  • 每次客户旅程发生变化时,渠道 API 都会发生变化。通道 API 之间的可重用商不是很高。它通常在 10-15% 之间。例如,如果在 Android 和 iOS 应用程序中映射了类似的客户旅程,那么相同的 API 就有可能被重用。
  • 通道 API 往往没有业务逻辑或任何服务编排逻辑,因为这些问题往往由流程 API 层处理。
  • 诸如安全性(CQRS、CORS)、身份验证、授权、限制等问题在 API 网关级别处理,而不是传递到通道 API 层。
  • 有时,在 API 开发过程中,人们可能会对 API 进行如此严格的区分和定义。但在许多应用程序迭代过程中,API 中开始出现这种差异,人们可以开始看到应用程序向这些分类移动。
  • 接下来,我们将介绍适用于我们看到的三个分类的 API 设计指南。

API 设计指南

一旦确定了正确级别的 resource 粒度,API 设计指南的其余部分将帮助制定正确级别的合同/接口以实现消费、可重用性和可维护性。

RESTful 客户端应该能够发现访问 URI 路径所需的所有可用操作 资源。客户端应该能够处理以下内容:

  • Request:处理发往服务器端的入站处理消息
  • Response:服务器提供的封装信息
  • Path:被请求资源的唯一标识符
  • 参数:作为键/值对添加到请求中的元素,用于指定对请求进行过滤、子集等操作

在我们开始设计 API 时,我们将分享多年来遇到的一些最佳实践。

命名和关联

资源名称通常指的是从 business 域中提取的名词。一旦确定了名词,API 合约就可以建模为针对这些名词的 HTTP 动词:

  • 资源的选择需要考虑细粒度与粗粒度模型。太细粒度意味着太多的闲聊,粗粒度意味着缩小焦点,从而支持变化。可以通过在一定程度上使用系统与流程 API 模型来推断这一点。但是问题就变成了,如果资源过于细粒度,系统 API 的数量就会增加,从而导致无法维护的复杂性。
  • API 是通过查看消费者的需求来设计的。根据客户旅程以及他们将如何映射到底层数据存储来派生您的 API 需求。这意味着,使用顶级设计方法查看 API 设计。首先使用底层模型进行数据建模,可能不会产生正确的平衡。如果您有现有的企业资产,您将需要执行一种中间相遇的方法,您需要通过编写有助于弥合差距的流程 API 来平衡客户的需求。

资源的基本 URL

这取决于您如何对待资源——作为单例还是作为集合。因此,理想情况下,您最终会得到一个资源的两个基本 URL,一个用于 collection,另一个用于实体。例如:

资源

POST

创建

GET

阅读

PUT

更新

删除

删除

/orders

创建新订单

订单

替换为新订单

错误(不想删除所有订单)

/orders/1234

错误

显示带有 ID 的订单:1234

如果存在更新订单;如果没有,创建一个新订单

删除ID为:1234的订单

处理错误

使用 standard HTTP 状态码来指示问题/错误:

  • 如果使用 JSON,错误应该是顶级属性
  • 有错误——描述性、正确性和信息性

示例错误消息如以下代码段所示:

{ 
   "type": "error", 
   "status":400, 
   "code": "bad_request", 
   "context_info": { 
         "errors": [ 
         { 
               "reason": "missing_argument", 
               "message": "order_id is required", 
               "name": "order_id", 
               "location": "query_param" 
         } 
         ] 
   }, 
   "help_url": "http://developers.some.com/api/docs/#error_code", 
   "message": "Bad Request" 
   "request_id": "8233232980923412494933" 
} 

HTTP 代码使用示例如下:

  • 400 错误请求
  • 401未经授权
  • 403 禁止
  • 404 未找到
  • 409 冲突
  • 429 请求过多
  • 5xx API 有问题

版本控制

服务版本控制有多个模型

  • URL:您只需添加 API version 进入 URL,例如: https://getOrder/order/v2.0/sobjects/Account。经常使用,但不是很好的做法。
  • Accept header:你修改accept header来指定版本,例如: Accept: application/vnd .getOrders.v2+json。客户端很少使用且麻烦
  • Schema level:使用 validation模式,难以使用 JSON 强制执行,并且适用于 XML。好习惯/罕见。
  • API外观层:使用外观层隐藏版本复杂度 来自客户端。

请记住,资源是一种语义模型;资源的表示形式和状态可能会随着时间而改变,但标识符必须始终如一地处理相同的资源。因此,只有在概念发生潜在变化时才应使用新的 URI。 API 外观层可以从底层服务和模式版本中抽象出北向 API。 API 管理平台支持创建 API 外观层。

分页

使用带有分页 information 的 URL 来处理结果偏移和限制。例如,/orders?limit=25&offset=50

属性

API 应支持提供消费者使用 查询参数模型提出的数据属性。例如,/orders?fields=id,orderDate,total

数据格式

API 应根据消费者的要求提供对 multiple 数据格式的支持。例如,/orders/1234.json 返回 JSON 格式的数据。

客户端支持有限的 HTTP 方法

根据设备及其支持 HTTP 动词的有限能力,您可能希望使用以下方法提供对 HTTP 方法的支持:

  • 创建/orders?method=post
  • 阅读/orders
  • 更新/orders/1234?method=put&location=park
  • 删除/orders/1234?method=delete

身份验证和授权

REST 服务在适当时为每个公开的方法使用基于角色的成员资格,并提供独立启用 GETPOST、< code class="literal">PUT 和 DELETE 基于任意数量的特定角色。

通常,应该在 API 网关级别处理。您不应该将此作为服务的一部分来处理。

端点重定向

由于业务或技术原因,服务库存可能会随时间而变化。它可能可以同时替换对旧端点的所有引用。

通过采用这种设计实践,服务端点的消费者可以适应服务库存的重构。它会自动将访问过时端点标识符的服务消费者引用到当前标识符:

读书笔记《cloud-native-applications-in-java》API设计最佳实践

HTTP 本身支持使用 3xx 状态代码和标准标头组合的端点重定向模式:

  • 301永久搬家
  • 307临时重定向
  • 位置/新URI

内容协商

服务消费者可能会以一种向后兼容的方式更改他们的要求。一项服务可能必须同时支持新老消费者,而不必为每种消费者引入特定功能。

作为其调用的一部分,服务可以指定在运行时协商的服务能力接受或返回的特定内容和数据表示格式。服务契约是指多种标准化的媒体类型。

安全的

始终使用 SSL 来保护 URI。 SSL 确保有保障的加密通信,从而简化了身份验证工作——无需签署每个 API 请求。

这涵盖了 API 设计中的一些最佳实践。可以从 Google、Facebook 和 Amazon 如何定义其公共 API 并将其用作 API 设计的基础来学习。

API 建模


有两个标准在竞争来描述 API——开放 API 和 RESTful API。我们将在以下部分中详细讨论它们。

开放式 API

Open API 计划的重点是创建和推广基于 Swagger 规范的供应商中立 API description 格式。 Open API 规范允许我们为 REST API 定义一个标准的、与语言无关的接口,它允许人类和计算机在不访问源代码的情况下发现和理解服务的功能。

在下图中,我们描述了基于 Open API 的示例 API 定义以及各个部分:

读书笔记《cloud-native-applications-in-java》API设计最佳实践

代码在下图中继续:

读书笔记《cloud-native-applications-in-java》API设计最佳实践

代码在下图中继续:

读书笔记《cloud-native-applications-in-java》API设计最佳实践

RESTful API 建模语言 (RAML)

RESTful API 建模语言 (RAML) 是标准的language 来描述 RESTful API。 RAML 的编写方式与 YAML 相同,YAML 是一种人类可读的数据序列化语言。 RAML 的目标是提供描述 API 所需的所有必要信息。 RAML 提供一种机器可读的API设计,可以被各种API管理工具读取。

在下图中,我们描述了一个示例 RAML 以及各个部分:

读书笔记《cloud-native-applications-in-java》API设计最佳实践

RAML 映射到完整的 API 设计生命周期,可分为以下几类:

读书笔记《cloud-native-applications-in-java》API设计最佳实践

让我们看一下流程:

  1. 设计:API 供应商提供编辑器作为 API 开发 套件可帮助设计/编写 API/RAML 定义,从而加快开发速度并减少错误。生成的 RAML 可以用模拟数据进行扩充,并允许与业务所有者/消费者进行设计迭代,以进行验证和正确性。
  2. Build:生成的 RAML 提供API 构建规范。开发套件可以基于 RAML 生成存根以插入逻辑。
  3. Test:RAML 可用于生成测试脚本。 Postman 和 Abao 等工具允许导入 RAML 规范并测试用于验证 API 的生成。此外,API Fortress 和 SmartBear 等工具还可以测试响应延迟、有效负载和错误。
  4. DocumentRAML 规范可以是转换为基于 HTML 的模型。诸如用于 PHP 的 RAML2HTML、API 控制台等工具提供了一种简单的方法来公开作为 RAML 的一部分指定的文档。该模型允许规范中的任何更改反映在文档中并保持同步。
  5. 集成:API生命周期的最后阶段 是集成或使用 API 的能力。 RAML 的使用允许供应商/工具创建多种方式来集成和使用 API。使用 RAML,可以构建特定于 API 的 SDK。供应商还提供可以利用 RAML 与客户端逻辑集成的工具。

两种标准之间的选择取决于组织选择的 API 网关产品堆栈。尽管每种产品都声称支持这两种标准,但大多数产品都偏爱一种标准。

API 网关部署模型


API 网关提供了一个外观 pattern,它封装了系统的内部工作,为所有传入的客户端提供了一个单一的入口点。 API 网关可以为每种类型的客户端提供量身定制的 API,同时解决诸如安全、身份验证、授权、限制、负载平衡等问题。

让我们看看影响 API 如何在 API 网关上部署的因素。

  • 客户端或渠道类型:取决于设备或渠道 请求来自哪里,API 可能需要提供不同的数据子集。例如,与移动客户端相比,服务的桌面版本可能会要求提供更多详细信息。即使在手机和平​​板电脑之间,数据也可能存在差异。我们如何确保相同的微服务可以服务所有设备类型的请求并且仍然可以处理这些变化?在这种情况下,我们为不同的设备类型创建了多个 API,以满足客户端的特定需求,而不会打扰微服务。
  • 数据转换:有时,服务 后端是为服务 JSON 内容而构建的。需求源于请求 XML 响应,反之亦然。在这种情况下,API 网关公开一个 API,该 API 提供 XML 作为响应,同时在网关级别进行数据转换,允许服务在没有任何更改或了解客户端需求的情况下工作。
  • 版本控制:对于公共 API 或绑定到未将版本控制添加到 URI 的资源的 API,API 网关可以路由传入的请求基于客户端和用于正确服务的版本。在这种情况下,API 网关可以使用多种技术破译 service 版本:
    • 客户端标识符可用于识别他们是否已移至新版本或正在使用旧版本。
    • 客户端可以根据 SLA 分为多个类别。当新版本发布时,可以要求最低类别或低使用率的客户端移动到新版本。随着客户端升级,API 网关可以将它们重定向到正确的服务版本。
  • 编排:有时,API 可能需要调用多个后端服务并汇总结果。在这种情况下,API 网关必须同时调用多个服务并聚合结果。有时,service 调用之间可能存在依赖关系。例如,传入的请求可能需要在实际服务调用之前进行身份验证,或者可能需要提取额外的客户端或会话信息来调用调用。由于某些产品提供运行时支持,因此可以在 API 网关层编写整个编排逻辑。另一种选择可能是编写一个流程 API,它在其他服务之间进行编排,并提供一个统一的 API 以供使用。从客户的角度来看,这有助于减少闲聊并提高整体性能。我们在第 3 章中介绍了编排模式,设计您的云原生应用程序
  • 服务发现:随着服务实例的上升和下降,service 注册表是在任何给定时间点可用的服务端点方面唯一真正的数据源。 API 网关应该能够调用服务注册表以在运行时获取服务端点并使用它来调用服务。服务注册表可以用作一种机制,在已注册的服务实例之间对服务调用进行负载平衡。
  • 处理超时:对于在合理时间内未响应的服务,API 网关允许您将请求超时。这允许网关处理超时故障,甚至为客户端提供故障模式。一种选择是提供缓存数据(如果适用并且取决于 service 的类型)或快速失败模型,其中网关可以在不调用服务的情况下立即返回错误或失败。
  • 数据缓存:API网关也可以缓存service 提供静态数据或不经常更改的数据的调用。此模型允许减少服务实例上的流量。这提高了整体响应延迟和整体系统弹性。缓存的数据也可以用作辅助故障流,以防主要流发生故障。
  • 服务调用服务正在部署可以使用多个接口或协议。例如,您可能拥有使用基于异步消息传递机制的服务(例如 JMS、MQ、Kafka 等),或者其他可以使用同步模型(例如 HTTP 或 Thrift)的服务。 API 网关应该能够支持多种服务调用模型,并在这些调用方法之上提供编排模型。
  • 服务计量/限制:对于某些类别的客户,您可能希望限制他们可以进行的服务调用次数。例如,如果您要提供 service 的免费增值模型,但功能有所减少,并且可以在一个时间框架。根据客户端类型(免费或付费)计量和限制传入请求的能力有助于围绕您的 API 和底层服务提供业务模型。如果您正在向另一个 SaaS 提供商进行外部 API 调用,这也很有帮助,通过 API 网关路由这些调用可以帮助预测/管理传出调用的数量,并在使用账单出现时产生不必要的冲击。
  • API 监控:另一个重要 问题是监控您的 API 调用是否有任何偏差,无论是在各个百分位的响应延迟、故障率、API 可用性等方面。这些指标需要用适当的警报和通知系统绘制在仪表板上。根据故障类型,可以自动化恢复脚本来克服它们。

这总结了可以应用于 API 网关以将您的服务作为 API 公开给消费者的各种使用场景和模式。

概括


在本章中,我们看到了如何根据 API 的主要用途和底层资源将 API 分类为不同的模型。我们看到了关于整体 API 设计的最佳实践以及可用于通过 Open API 或 RAML 规范对 API 建模的标准。接下来,我们看到了如何利用 API 网关来解决未在服务级别处理的问题。

下一章,我们将介绍云开发对企业现有格局的影响,以及如何实现向数字化企业转型。