这篇文章主要讨论了构建高效、可靠API的不同设计方法,并对每种方法进行了详细的分析。作者将每种API设计方法分为四个部分:介绍、优点、缺点和使用场景。以下是每种API设计方法的简要总结:
-
REST(表述性状态转移)
- 介绍:REST是一种架构风格,用于描述客户端和服务器之间通信的规范。
- 优点:灵活性高,易于理解和集成。
- 缺点:单向通信,高度依赖于HTTP/1,难以在大型系统中支持。
- 使用场景:适用于各种复杂程度的系统,特别是电子商务应用和后端操作。
-
gRPC
- 介绍:gRPC是由Google开发的框架,基于HTTP/2协议,支持远程过程调用(RPC)。
- 优点:速度快,支持双向数据流。
- 缺点:可读性差,处理二进制数据复杂,不适合大型文件传输。
- 使用场景:适用于需要高效性能和强类型数据模型的微服务架构。
-
GraphQL
- 介绍:GraphQL允许客户端构建查询或变异并将这些信息发送到服务器。
- 优点:灵活的API配置,支持实时更新。
- 缺点:学习曲线长,对于简单系统来说过于复杂。
- 使用场景:适用于复杂系统,特别是需要灵活数据检索和实时更新的场景。
- EDA(事件驱动架构)
- 介绍:EDA允许订阅特定类型的消息。
- 优点:消除依赖,易于扩展。
- 缺点:复杂系统中可能变得混乱,协议和实现的多样性可能带来复杂性。
- 使用场景:适用于需要异步通信的系统,特别是在不需要保证100%消息传递的情况下。
这篇文章主要面向需要为新系统选择合适API设计方法的人。
这张照片由 Lance Anderson 拍摄,来自 Unsplash。
如何找到构建真正方便和可靠的API的最佳途径?有许多因素需要考虑,如性能、版本管理、安全性、易用性及用户友好性、可支持性、复杂性等。工程师往往在刚开始时就面临选择困难,不知道应该选择什么以及如何正确设计API。这总是一个选择的问题。
我不打算详细描述每种API设计方法。我的目标是总结每种方法中最重要的方面,并将其与适用的业务场景关联起来。另外,需要注意的是,如果没有提到某个方面,比如安全或缓存,这表示这些方面都没有问题。我认为这些方面不是额外的好处,而是必备的要素。
我将每种API设计方法分为几个部分:优点、缺点和应用场景。在介绍部分,我提供了基本信息,并附上了更多详细信息的链接。如今,互联网上充斥着各种数据;你唯一需要知道的是如何整理这些数据并找到你需要的信息。
请不要将API设计风格与API协议混淆,不同的API设计风格可能使用相同的底层协议。
我相信在你犹豫的时候,看看你的利弊列表会帮助你做出更好的选择。别光顾着比较利弊的数量,因为这不只是一些数字的问题。你需要权衡每个要点的关键因素,并根据你的需求来调整。我知道有时候你可能觉得我在比较苹果和橘子,我在每个例子中都会尽量解释清楚。
瑞典休息 (Note: Given the context and the expert suggestions, a more accurate translation would be keeping it technical or casual. However, "REST" could also be a specific term in context, so a more literal translation that maintains the code term style could be "# REST" or "# 休息" as suggested. Given the original suggestion to keep it informal and match the style of colloquial Chinese, I will use "# 休息".) 瑞典休息 瑞典保持原样 (更准确地反映源文本的技术或非正式风格,应保持不变) 休息这张照片由 Sincerely Media 拍摄,来自 Unsplash
简介
这是一组描述如何正确构建客户端与服务器之间通信的架构原则。存在一个普遍的误解是REST必须仅使用HTTP/1协议,或者REST和HTTP是同一件事。实际上,事实并非如此。Roy Fielding在2000年提出了REST架构风格的概念。这是他的dissertation,在第五章中详细描述了基本原则。在5.3.2节中,你可以了解到REST并不限制特定协议的通信。使用HTTP,特别是HTTP/1.0或HTTP/1.1,只是REST的事实上的标准。
尽管我可以使用自定义协议与REST一起使用,但是也可以在API不是RESTful的情况下使用HTTP协议。我可以基于HTTP/2甚至HTTP/3来创建RESTful API,这意味着我无法直接比较REST和gRPC的性能表现。这听起来可能有些让人困惑。但是,当我提到RESTful API时,是指基于HTTP/1的API;否则,结果可能就不一样了。就像其他架构方法一样,REST也有它自己的原则。
- 客户端/服务器架构
- 无状态
- 缓存
- 统一接口
- 分层架构
- 按需加载代码
这些原则在Roy Fielding的博士论文和其他网站中都有详细的阐述,我不想重复这些内容。这些原则指导你设计API并创建系统中的端点以避免问题。你也可以在这里找到一个很好的解释:这里。
优点
REST架构非常灵活,可以适应各种系统,无论系统多么复杂。当你依据统一接口原则设计端点时,你可以轻松构建实体的API,并可以轻松扩展系统规模,满足任何需求。
2) REST API易于阅读和理解,即使你不具备编程背景。工具如Swagger允许你浏览你的HTTP API并查看所有参数。如果你采用REST风格,你的端点会更加清晰易懂。
3) 非常受欢迎,尤其是在全球范围内,使得与你的API进行集成变得更加容易。如果你有一个平台,并且需要提供一个开放API或与某些用户共享你的API,并且根据REST原则设计了你的请求结构,那么其他开发者很容易就能开始集成。
4) REST 不依赖特定的语言和协议。这种架构风格本身不依赖任何特定的语言或协议。虽然最初提议用HTTP/1作为例子,但这并不是强制要求。你也可以将你的 API 迁移到HTTP/2或HTTP/3,它依然保持RESTful特性,但会带来额外的优势。
缺点:
1) 单向通信方式。REST 只是从客户端向服务器发送请求,并且它最初是为了解决某些问题而设计的。不幸的是,如果你需要实时信息或双向通信,你可能需要寻找其他的API解决方案。REST 并不适合这些情况。
2) 实际上高度依赖于HTTP/1。虽然你可以用HTTP/2和HTTP/3构建RESTful API,人们通常期望使用HTTP/1,从而继承了它所有的缺点,包括但不限于延迟、错误处理不完善等。在这种情况下,你的API涉及B2B整合时尤为相关,此时客户可能从开发角度还未准备好切换到HTTP/2或HTTP/3。
3) 许多被标为 RESTful 的 API 并不真正 RESTful。因为与 HTTP 的紧密关联,许多非 RESTful API 也被认为是 RESTful。初学者很容易采用这种方法。
4) 在大型系统中难以维护。随着应用的增长,接口的数量可能会变得很大,这使得在某些接口实现中难以避免代码重复,特别是当你的应用程序中有大量的资源时。
应用场景
REST适用于几乎所有复杂度级别的系统,因为其入门门槛很低,并且API可以扩展到任何规模。它需要最少的配置,使得应用程序原型可以在几天内完成。它非常适合电子商务应用和后台操作。当你需要发出请求并查看结果时,REST是一个理想的选择。手机上的大多数应用程序使用REST与服务器通信。除非你需要非常强大的性能,或者你的应用程序必须频繁地与服务器进行双向交互,REST是考虑的最佳选项之一。
gRPC (gRPC协议)介绍
gRPC 不是一种架构风格,而是一个由 Google 创建的框架,基于 HTTP/2 协议。该框架基于 RPC 的理念,允许客户端调用服务器上的特定函数。调用方式可以是同步或异步,每个函数的输入和输出都定义在 proto 文件中。gRPC 使用 Google Protobuf 进行数据序列化,这意味着数据以二进制形式在客户端和服务器之间传输。它支持四种通信方式。
- 一元RPC
- 服务端流式RPC
- 客户端流式RPC
- 全双工流式RPC
根据您的需求,您可以支持知名的需求响应通信模式,或者双向流传输数据。想要了解更多,您可以访问gRPC的官方站点。
优点:
速度。该框架使用 HTTP/2 协议,可以同时处理多个请求。然而,这种速度优势依赖于您将 gRPC 与什么进行比较。例如,相比于 HTTP/3,gRPC 可能较慢,但与 HTTP/1 相比,它更快。HTTP/2 在客户端和服务器之间建立持久连接,并在此连接上共享请求,从而减少延迟并提高性能。
2) 双向数据流。gRPC 支持双向数据流,允许数据在两个方向上传输。你也可以通过客户端调用来触发流,这对于实时传输数据块特别有帮助。
3) 轻量级的消息。使用 Protobuf,gRPC 可以使负载大小显著减少约 30%。然而,这一优势并不只属于 gRPC,你可以配置 Protobuf 与 REST、HTTP/1 或甚至 WebSockets 一起使用。
4) 具有强类型的消息。你可以设置每个属性所占用的内存大小,有助于避免不必要的数据传输,使消息更加精简。
不足
1) 可读性方面的问题。gRPC 中每个方法都需要定义请求和响应的消息格式。这意味着不同的函数需要不同的消息格式。随着端点数量的增多,消息模型可能会变得复杂且难以管理起来。消息可能分散在多个文件中,导致很难将请求与相应的函数关联起来,尤其是在代码不够整洁的情况下。
2) 负载均衡问题。gRPC 的负载均衡仅在客户端完成。它利用 TCP 层的持久连接并通过该连接发送进一步的请求,而标准的微服务负载均衡工具对此无能为力。要实现请求级的负载均衡,需要在客户端实现负载均衡。这需要管理到不同服务器的持久连接池,并在它们之间分配请求,或者为每个请求创建一个新的持久连接,这可能会影响性能。
3) 处理二进制数据。在使用 Protobuf 的解决方案中,处理二进制数据是一个常见的问题。没有适当的转换,理解负载内容会比较困难。不过,有一些工具可以帮上忙,不过,这些工具需要你单独安装和配置,这会让整个设置过程变得复杂得多。
4) 无法从浏览器进行调用。有些现代浏览器不直接支持 gRPC 调用,这不仅让调试变得更复杂,也让直接从浏览器使用 gRPC 变得更加棘手。未来浏览器支持的改进可能会解决这个问题。
5) 文件传输。虽然 gRPC 支持文件传输,但这种用途并不常见。文件大小在 1 MB 到 4 MB 之间时,简单的 HTTP 文件传输通常比 gRPC 更快,因为 gRPC 的序列化和反序列化过程会增加额外的开销。
6) 实时更新对简单场景来说太复杂了。gRPC 支持流式传输以实现实时更新,但在简单的场景中可能难以使用。管理 gRPC 流的开销和复杂性可能超过其带来的好处,因此,使用更简单的解决方案,如 WebSocket 集成,可能更加实际和高效。
应用场景
gRPC 用于需要强类型通信数据模型的场景,以确保快速高效的性能,这使其非常适合微服务架构。可以在仓库中查看 proto 消息,使服务间的合约更加清晰,并且端点无需对外暴露。由于 Protobuf 消息的压缩,传输大小比其他负载减少 30%。另外,对于内部通信,您无需使用 REST 或其他 API 风格的优势。在横向扩展服务时,确保客户端能够正确负载平衡 gRPC 请求。如果您考虑在前端和后端之间使用 gRPC,需要充分的理由,尤其是与性能相关的。例如,您可能需要重型通道或大请求负载的数据流,此时 Protobuf 序列化可能带来好处。
GraphQL简介
很难用几句话来描述 GraphQL 的核心。简单来说,GraphQL 让你在客户端构建基于文本的查询或变异,并把这些信息发给服务器。服务器收到信息后,会发送给 GraphQL 服务器,后者会解析这些文本,并根据模式调用相应的解析器。查询用于检索信息,而变异则用于更改数据(创建、更新、删除)。解析器是用来定义如何获取和过滤数据的函数。模式定义了所有数据类型和它们之间的关系。此外,客户端可以订阅数据,从而实现实时更新。GraphQL 是一个简单的接口,没有特定的协议要求。虽然 HTTP 很常见,但你可以使用任何协议,而 WebSockets 则常用于订阅。如需了解更多信息,请访问 GraphQL 的官方站点。
好处
1) 协议无关。GraphQL 不依赖于任何特定的协议,因此你可以使用 HTTP/1、HTTP/2 或 HTTP/3。甚至可以选择其他协议。你只需要正确配置 GraphQL 服务器即可。这让你可以根据自己的偏好来配置。
2) 灵活的 API 设置。GraphQL 支持过滤和嵌套查询,允许你根据客户端请求仅获取相关数据。无需传输不必要的数据,从而使请求更轻量化,从而可能提高性能。你可以轻松调整请求以检索你需要的确切信息。这种灵活性有助于你避免创建数百个端点,其中服务器上的许多数据和逻辑部分会重复。
3) 实时更新功能。您可以订阅任何更改并定期收到更新。这是一大优势,使您的系统更加完善。不过,您需要考虑数据量,因为这类更新通常使用WebSocket,需要遵守协议并控制数据量。
不足
1) 学习曲线较长,因为 GraphQL 比较复杂。理解基本原理并构建高效的 API 需要一段时间。你需要设置模式,配置 GraphQL 服务器,定义解析器,并确保 API 能够正常运行。虽然这完全是可能的,但在不同上下文中切换可能会有些困难。
2) 缓存。尽管你可以根据需要配置你的请求,但底层请求将保持不变,这使得在网络层实现缓存变得不可能。你将需要在服务器端优化缓存,可能需要使用诸如持久查询之类的已建立的 GraphQL 解决方案。
3) 为简单的系统过度工程设计。GraphQL 需要预先配置和额外的资源才能构建一个健壮的 API。对于简单的系统来说,这种复杂性可能是不必要的。使用简单的 HTTP,你可以在几个小时内看到结果,但使用 GraphQL,你可能需要更多经验才能达到同样的速度。
4) 大量请求。虽然 GraphQL 允许避免传输不必要的数据,但由于需要额外参数,其负载通常会比简单的 HTTP 请求更重。尽管这种差异一般只有几 KB,但如果请求量很大,这种差异就可能产生影响。
应用场景
GraphQL 适合资源数量可能大幅增加的复杂系统。除非你对 GraphQL 非常熟悉,并且清楚如何运用它,否则我不建议在初创公司中使用它。否则,你将花费大量时间来正确配置 GraphQL。GraphQL 的优雅之处在于它能够轻松地定义你从服务器获取的数据,而不获取冗余信息。与 gRPC 不同的是,你不需要为了获取更多或更少的数据而重复定义响应消息。你可以轻松使用嵌套查询来过滤数据,排除不必要的信息。此外,你可以将实时更新与简单端点结合,保持单一的 API 风格。在复杂系统中,由于端点数量庞大,维护 REST 架构会变得很困难,但 GraphQL 可以让你的端点更加优雅和易于管理。
探索性数据分析 (EDA)Chris Blonk 在 Unsplash 上发布的一张照片
简介
我对将Event Drive API加入这个列表有些疑虑,但最终决定它是重要的一部分。EDA允许你订阅特定类型的消息,并据此构建你的系统。有不同类型的消息和不同的方法来适当地设计你的系统,使其与这些消息相适应。例如,你可以采用事件溯源的方式,从而可以重现业务流程。有成百上千的消息代理和各种规模的框架可以帮助你操作你的业务逻辑。各种协议,如AMQP、MQTT等,规定了如何获取数据和传播事件。本质上,当你需要事件指示某些事情发生时,就可以使用EDA。最好使用异步事件,因为同步事件会导致高负载和不必要的依赖。
优点:
1) 消除依赖。异步事件确实是无需等待响应即可通信的最佳方式。这种方法有很多好处。即使系统其他部分出现故障,您的服务仍能继续运行。您无需担心事件是否立即传递,也不必依赖系统其他部分的变化,只要事件消息不变。
2) 多种协议和实现。有许多可供选择的协议和框架,其中许多是开源的。您可以根据需求选择最适合的。Kafka、RabbitMQ、NATS,甚至 Redis 都有发布-订阅实现。每个系统都配有详尽的文档,并且提供了多种语言的库支持。
3) 易于扩展。许多消息代理擅长扩展。只要集群配置准确,通常不会遇到扩展问题。大多数消息代理都支持水平扩展,很难想象会有负载压垮你的资源。
缺点
1) 难以检查消息类型。事件消息的文档往往不齐全,描述可能藏在仓库的某个角落。不同的消息可能散布在仓库的各个角落,不像 Swagger 那样有一个集中的视图。然而,如果开发人员共同努力,这种情况是可以改善的。
2) 在一个复杂的系统中可能会变得一团糟。配置事件最初可能非常有帮助,因为你的服务边界是解耦且独立的。然而,随着系统的增长,保持清晰的事件通信变得具有挑战性。缺乏一个强有力的协议,事件结构可能会变成用户自定义的,这会导致发布者和订阅者之间的依赖关系呈指数级增长。这使得恢复到清晰结构的难度增加。数据平台的存在就是为了应对这一问题,通过聚合所有事件,允许任何服务直接查询数据平台中的所有事件。
3) 多种协议和实现方式。这既是优势也是劣势。虽然拥有广泛的选择是有益的,但这些解决方案的技术栈各不相同。你可能需要花时间来了解 Kafka 和 RabbitMQ 的区别,确定哪一种更适合你的场景,需要集成哪些库,以及如何部署和扩展这些解决方案。你的开发人员和 DevOps 团队可能缺乏某些解决方案的经验,或者需要时间来评估各自的优缺点。如果没有足够的了解,可能会出现设计上的疏忽,导致设计上的缺陷或需要寻找备用方案。
应用场景
几乎所有的系统在某个时刻都会转向事件驱动架构。当你需要通知其他服务且不需要等待响应时,EDA 是一个很好的选择,特别是当不需要确保每个事件都能被100%送达时。比如,在电子商务应用中,当客户下单后,服务会发出一个事件,其他服务可以相应地处理其业务逻辑。
然而,人们有时会将事件与指令混淆,尤其是在需要确保消息传递的情况下。当需要通知服务并确保操作完成,否则无法继续时,你应该考虑使用HTTP请求、gRPC或其他方法。这是因为你的业务逻辑高度依赖于不同服务的响应。例如,你不能在不清楚余额已减少的情况下发起提现事件并继续进行购物流程。在这种情况下,需要进行同步操作。
这个问题为什么我没有提到SOAP我坚信SOAP主要被用于旧的企业应用中。SOAP基于XML,因此相对较重。现在SOAP架构的支持情况不佳,你可能会发现找到合适的库比较困难。在很多年前,可访问的协议比较稀缺时,SOAP是已知的解决方案,但今天它已经过时。只有在必须支持旧应用且变更风险较大时,才应该考虑使用SOAP。这篇文章是为了需要在新系统中选择方法的人而写的,因此在列表中添加SOAP是多余的。