System Design 系统设计 (系统架构与设计方法)


原文引用 – System Design


Chapter III 系统架构与设计方法



N-tier architecture N层架构

N 层体系结构将应用程序划分为逻辑层物理层

层是分离职责和管理依赖关系的一种方式。 每一层都有特定的职责。

高层可以使用低层的服务,反之则不行。

层在物理上是分开的,在不同的机器上运行

一个层可以直接调用另一个层,或者使用异步消息传递。

虽然每一层都可以托管在它自己的层中,但这不是必需的。 多个层可能托管在同一层上。 物理分离层提高了可扩展性和弹性,并增加了额外网络通信的延迟。

N 层架构可以有两种大类:

在`封闭`层架构中,一层只能立即向下调用下一层。

在`开放`层体系结构中,一个层可以调用它下面的任何层。

封闭层架构限制了层与层之间的依赖关系。 但是,如果一层只是将请求传递到下一层,它可能会产生不必要的网络流量

N 层架构的类型

  • 三层架构 3-Tier architecture

3-Tier 被广泛使用,由以下不同的层组成:

表示层:处理用户与应用程序的交互。

业务逻辑层:接受来自应用层的数据,根据业务逻辑对其进行验证并将其传递给数据层。

数据访问层:接收来自业务层的数据,并对数据库进行必要的操作。
  • 2层架构 2-Tier architecture

在此体系结构中,表示层在客户端上运行并与数据存储进行通信。 客户端和服务器之间没有业务逻辑层或直接层

  • 单层或一层架构 1-Tier architecture

它是最简单的一个,因为它相当于在个人计算机上运行应用程序。 应用程序运行所需的所有组件都在单个应用程序或服务器上。

优缺点:

优点

可以提高可用性。

更好的安全性,因为层可以像防火墙一样运行。

单独的层允许我们根据需要扩展它们。

改进维护,因为不同的人可以管理不同的层级。

缺点

增加了整个系统的复杂性。

随着层数的增加,网络延迟增加。

昂贵,因为每一层都有自己的硬件成本。

网络安全难以管理。


Message Brokers 消息代理

消息代理是一种软件,它使应用程序、系统和服务能够相互通信交换信息

消息代理通过在正式的消息传递协议之间转换消息来做到这一点。 这允许相互依赖的服务直接相互“对话”,即使它们是用不同的语言编写的或在不同的平台上实现的。

消息代理可以验证、存储、路由消息并将消息传递到适当的目的地。

它们充当其他应用程序之间的中介,允许发送者在不知道接收者在哪里、他们是否活跃或有多少人的情况下发布消息。 这有助于系统内流程和服务的解耦

消息代理的模式

  • 点对点消息传递 Point-to-Point messaging

这是消息队列中使用的分发模式,在消息的发送者和接收者之间具有一对一的关系

  • 发布-订阅消息传递 Publish-subscribe messaging

在这种消息分发模式中,通常称为“发布/订阅”,每条消息的生产者将其发布到一个主题, 多个消息消费者订阅他们想要从中接收消息的主题

消息代理与事件流 Message brokers vs Event streaming

消息代理可以支持两种或多种消息传递模式,包括消息队列和发布/订阅

事件流平台仅提供发布/订阅式分发模式。

事件流平台专为处理大量消息而设计,易于扩展。

他们能够将记录流排序为称为主题的类别,并将它们存储预定的时间。 然而,与消息代理不同的是,事件流平台无法保证消息传递或跟踪哪些消费者收到了消息。

事件流平台提供比消息代理更多的可扩展性,但确保容错的功能更少

例如消息重新发送,以及更有限的消息路由和排队功能。

消息代理与企业服务总线 (ESB) Message brokers vs Enterprise Service Bus (ESB)

企业服务总线 (ESB) 基础设施很复杂,集成起来具有挑战性且维护成本高昂。 当生产环境出现问题时很难对其进行故障排除,它们不容易扩展,更新也很繁琐。

消息代理ESB 的“轻量级”替代品,它以较低的成本提供类似的功能,一种服务间通信的机制。 它们非常适合用于随着 ESB 失宠而变得越来越流行的微服务架构

常用的消息代理

NATS

Apache Kafka

RabbitMQ

ActiveMQ


消息队列 Message Queues

消息队列是一种促进异步通信的服务到服务通信形式。

它异步接收来自生产者的消息并将它们发送给消费者。

队列用于有效管理大规模分布式系统中的请求。 在具有最小处理负载和小型数据库的小型系统中,写入速度可以预见地快。 然而,在更复杂和更大的系统中,写入可能会花费几乎不确定的时间

如何工作

消息存储在队列中,直到它们被处理和删除。 每条消息仅由一个消费者处理一次。

生产者 将作业发布到队列,然后将作业状态通知给用户。

消费者 从队列中取出作业,处理它,然后发出作业完成的信号。

使用消息队列的一些优点:

可扩展性:消息队列可以在我们需要的地方精确扩展。 
        当工作量达到峰值时,我们应用程序的多个实例都可以将请求添加到队列中而没有冲突的风险

解耦:消息队列消除了组件之间的依赖关系,显着简化了解耦应用程序的实现。

性能:消息队列支持异步通信,这意味着生成和使用消息的端点与队列交互,而不是彼此交互。 
     生产者可以将请求添加到队列中,而无需等待它们被处理。

可靠性:队列使我们的数据持久化,并减少系统不同部分离线时发生的错误。

消息队列的一些理想特性

  • 推送或拉取 Push or Pull Delivery

    大多数消息队列都提供用于检索消息的推送和拉取选项。

    拉取意味着不断查询队列中的新消息。 推送意味着当消息可用时通知消费者。

    我们还可以使用长轮询来允许拉取等待指定的时间量以等待新消息到

  • FIFO(先进先出)队列 FIFO (First-In-First-Out) Queues

    在这些队列中,最早的(或第一个)条目,有时称为队列的“头”,首先被处理。

  • 安排或延迟交货 Schedule or Delay Delivery

    许多消息队列支持为消息设置特定的传递时间。 如果我们需要对所有消息有一个共同的延迟,我们可以设置一个延迟队列。

  • 至少一次交付 Schedule or Delay Delivery

    消息队列可以存储多个消息副本以实现冗余和高可用性,并在发生通信故障或错误时重新发送消息以确保它们至少被传递一次。

  • 恰好一次交付 Exactly-Once Delivery

    当不能容忍重复时,FIFO(先进先出)消息队列将通过自动过滤掉重复项来确保每条消息恰好传递一次(且仅传递一次)。

  • 死信队列 Dead-letter Queues

    死信队列是其他队列可以向其发送无法成功处理的消息的队列。

    这使得将它们放在一边以供进一步检查变得容易,而不会阻塞队列处理或将 CPU 周期花费在可能永远不会被成功使用的消息上。

  • 顺序 Ordering

    大多数消息队列提供尽力而为的排序,以确保消息通常以与发送时相同的顺序传递,并且消息至少传递一次。

  • 毒丸消息 Poison-pill Messages

    毒丸是可以接收但不能处理的特殊消息。 它们是一种用于向消费者发出信号以结束其工作的机制,因此它不再等待新的输入,类似于关闭客户端/服务器模型中的套接字。

  • 安全 Security

    消息队列将对尝试访问队列的应用程序进行身份验证,这使我们能够通过网络以及队列本身对消息进行加密。

  • 任务队列 Tasks queues

    任务队列接收任务及其相关数据,运行它们,然后交付它们的结果。 它们可以支持调度并可用于在后台运行计算密集型作业。

背压 Backpressure

如果队列开始显着增长,队列大小可能会大于内存,从而导致缓存未命中、磁盘读取,甚至性能下降。 背压可以通过限制队列大小来提供帮助,从而为队列中已有的作业保持高吞吐率和良好的响应时间。 一旦队列填满,客户端就会收到服务器忙或 HTTP 503 状态代码,以便稍后重试。 客户端可以稍后重试请求,也许使用指数退避策略 exponential backoff strategy

一些广泛使用的消息队列

Amazon SQS

RabbitMQ

ActiveMQ

ZeroMQ


发布-订阅 Publish-Subscribe

类似于消息队列,发布-订阅也是一种促进异步通信服务到服务通信形式。

发布/订阅模型中,发布到主题的任何消息都会立即推送到该主题的所有订阅者。

消息主题的订阅者通常执行不同的功能,并且每个订阅者可以并行地对消息执行不同的操作。

发布者不需要知道谁在使用它广播的信息,订阅者也不需要知道消息来自哪里。

这种消息传递方式与消息队列略有不同,在消息队列中发送消息的组件通常知道它要发送到的目的地

如何工作

与消息队列不同,消息队列在检索消息之前对消息进行批处理,消息主题在传输消息时很少或没有排队,并立即将它们推送给所有订阅者。

  • 消息主题 Topic提供了一种轻量级机制来广播异步事件通知和端点,允许软件组件连接到主题以发送和接收这些消息。

  • 广播消息,称为发布者的组件只需将消息推送到主题。

  • 订阅该主题的所有组件(称为订阅者)将接收广播的每条消息。

发布-订阅的一些优点

消除轮询:消息主题允许即时的、基于推送的传递,消除了消息消费者定期检查或“轮询”新信息和更新的需要。
        这促进了更快的响应时间并减少了交付延迟,这在不能容忍延迟的系统中尤其成问题。

动态定位:Pub/Sub 使服务发现更容易、更自然且不易出错。 
        发布者不会维护一个应用程序可以发送消息的节点名册,而是简单地将消息发布到一个主题。
        然后,任何感兴趣的一方都会将其端点订阅到该主题,并开始接收这些消息。 
        订户可以更改、升级、增加或消失,系统会动态调整。

解耦和独立缩放:发布者和订阅者解耦并且彼此独立工作,这使我们能够独立地开发和扩展它们。

简化通信:发布-订阅模型通过删除所有点对点连接和消息主题的单个连接来降低复杂性,这将管理订阅并决定应将哪些消息传递到哪些端点。

发布-订阅的特征

  • 推送交付 Push Delivery

    当消息发布到消息主题时,Pub/Sub 消息传递会立即推送异步事件通知。 当消息可用时,订阅者会收到通知。

  • 多种交付协议 Multiple Delivery Protocols

    在发布-订阅模型中,主题通常可以连接到多种类型的端点,例如消息队列、无服务器函数、HTTP 服务器等。

  • 扇出 Fanout

    当一条消息被发送到一个主题,然后被复制并推送到多个端点时,就会发生这种情况。 Fanout 提供异步事件通知,从而允许并行处理。

  • 过滤 Filtering

    此功能使订阅者能够创建消息过滤策略,以便它只会收到它感兴趣的通知,而不是接收发布到主题的每条消息。

  • 耐用性 Durability

    Pub/Sub 消息传递服务通常通过在多个服务器上存储相同消息的副本来提供非常高的持久性,并且至少传递一次。

  • 安全 Security

    消息主题对尝试发布内容的应用程序进行身份验证,这使我们能够使用加密端点并对通过网络传输的消息进行加密。

发布订阅常用的一些技术:

Amazon SNS

Google Pub/Sub


企业服务总线 (ESB) Enterprise Service Bus (ESB)

企业服务总线 (ESB) 是一种架构模式,集中式软件组件借此执行应用程序之间的集成。

它执行数据模型的转换、处理连接、执行消息路由、转换通信协议,并可能管理多个请求的组合。

ESB 可以将这些集成和转换作为服务接口提供给新应用程序重用。

优缺点

从理论上讲,集中式 ESB 提供了标准化和显着简化整个企业服务之间的通信、消息传递和集成的潜力。

优点:

提高开发人员的工作效率:使开发人员能够将新技术整合到应用程序的一部分中,而无需触及应用程序的其余部分。

更简单、更具成本效益的可扩展性:组件可以独立于其他组件进行扩展。

更强的弹性:一个组件的故障不会影响其他组件,并且每个微服务都可以遵守自己的可用性要求,而不会危及系统中其他组件的可用性。

虽然 ESB 已在许多组织中成功部署,但在许多其他组织中,ESB 逐渐被视为瓶颈。

缺点

对一个集成进行更改或增强可能会破坏使用同一集成的其他人的稳定性。

单点故障可能会导致所有通信中断。

对 ESB 的更新通常会影响现有的集成,因此执行任何更新都需要进行大量测试。

ESB 是集中管理的,这使得跨团队协作具有挑战性。

配置和维护复杂度高。

一些广泛使用的企业服务总线 (ESB) 技术

Azure Service Bus

IBM App Connect

Apache Camel

Fuse ESB


整体式应用程序 和 微服务 Monoliths and Microservices

整体式应用程序 Monoliths

整体式应用程序是一个自包含且独立的应用程序。 它作为一个单元构建,不仅负责特定任务,而且可以执行满足业务需求所需的每个步骤。

优点

易于开发或调试。
快速可靠的通信。
易于监控和测试。
支持 ACID 事务。

缺点

随着代码库的增长,维护变得越来越困难。
紧密耦合的应用程序,难以扩展。
需要致力于特定的技术堆栈。
每次更新时,都会重新部署整个应用程序。
降低可靠性,因为单个错误可能会导致整个系统崩溃。
难以扩展或采用新技术。

模块化单体服务 Modular monoliths

模块化单体是一种我们构建和部署单个应用程序(即单体部分)的方法,但我们构建它的方式是将代码分解为独立模块,以实现应用程序所需的每个功能。

这种方法减少了模块的依赖性,例如我们可以在不影响其他模块的情况下增强或更改模块。 如果做得好,从长远来看,这确实是有益的,因为随着系统的增长,它降低了维护单体应用所带来的复杂性。

微服务 Microservices

微服务架构由一系列小型自治服务组成,其中每个服务都是独立的,并且应该在有界上下文中实现单一业务功能。 有界上下文是业务逻辑的自然划分,它提供了域模型存在于其中的显式边界。

每个服务都有一个单独的代码库,可以由一个小型开发团队管理。 服务可以独立部署,团队可以更新现有服务而无需重建和重新部署整个应用程序。

服务负责保存自己的数据或外部状态(每个服务的数据库)。 这不同于传统模型,在传统模型中,单独的数据层处理数据持久性。

  • 微服务架构风格具有以下特点:

    松耦合:服务应该是松耦合的,以便它们可以独立部署和扩展。 这将导致开发团队的分散化,从而使他们能够在最小的约束和操作依赖性的情况下更快地开发和部署。

    小而专注:这是关于范围和责任而不是规模,服务应该专注于特定问题。 基本上,“它只做一件事,而且做得很好”。 理想情况下,它们可以独立于底层架构。

    为企业而建:微服务架构通常围绕业务能力和优先级进行组织。

    弹性和容错:服务的设计方式应使其在发生故障或错误时仍能正常运行。 在具有可独立部署服务的环境中,容错能力是最重要的。

    高度可维护:服务应该易于维护和测试,因为无法维护的服务将被重写。

优点

松散耦合的服务。
服务可以独立部署。
对多个开发团队高度敏捷。
提高容错和数据隔离。
更好的可扩展性,因为每个服务都可以独立扩展。
消除对特定技术堆栈的任何长期承诺。

缺点

分布式系统的复杂性。
测试更加困难。
维护成本高(单个服务器、数据库等)。
服务间通信有其自身的挑战。
数据完整性和一致性。
网络拥塞和延迟。
  • 微服务最佳实践:

    围绕业务领域建模服务。

    服务应该具有松耦合和高功能内聚性。

    隔离故障并使用弹性策略来防止服务内的故障级联。

    服务应该只通过设计良好的 API 进行通信。 避免泄露实施细节。

    数据存储对于拥有数据的服务应该是私有的

    避免服务之间的耦合。 耦合的原因包括共享数据库模式和严格的通信协议。

    去中心化一切。 各个团队负责设计和构建服务。 避免共享代码或数据模式。

    通过使用断路器实现容错来快速失败。

    确保 API 更改向后兼容。

  • 微服务架构的一些常见陷阱:

    服务边界不基于业务领域。

    低估了构建分布式系统的难度。

    共享数据库或服务之间的共同依赖关系。

    缺乏业务一致性。

    缺乏明确的所有权。

    尝试做所有 ACID 而不是 BASE。

    缺乏容错设计可能导致级联故障。

当心分布式整体架构 distributed monolith

Distributed Monolith 是一个类似于微服务架构的系统,但在自身内部紧密耦合,就像一个整体应用程序。 采用微服务架构有很多优势。 但是在制作一个时,我们很有可能最终得到一个分布式整体架构。

如果以下任何一项适用,我们的微服务就是一个分布式整体架构:

需要低延迟通信。

服务不容易扩展。

服务之间的依赖关系。

共享相同的资源,例如数据库。

紧密耦合的系统。

使用微服务架构构建应用程序的主要原因之一是具有可扩展性

因此,微服务应该有松散耦合的服务,使每个服务都是独立的。

分布式整体架构消除了这一点并导致大多数组件相互依赖,从而增加了设计复杂性。

微服务与面向服务的架构 (SOA) Microservices vs Service Oriented Architecture (SOA)

面向服务的架构 (SOA),有时甚至可以与微服务互换,但它们彼此不同,两种方法之间的主要区别在于范围。

面向服务的体系结构 (SOA) 定义了一种通过服务接口使软件组件可重用的方法。 这些接口使用通用通信标准并专注于最大化应用程序服务的可重用性

微服务为各种最小的独立服务单元的集合,专注于团队自治和解耦。

为什么不需要微服务

虽然每种方法都有自己的优点和缺点,但建议在构建新系统时从单体开始。 重要的是要理解,微服务不是灵丹妙药,而是解决组织问题。 微服务架构与您的组织优先级和团队以及技术一样重要。

决定迁移到微服务架构之前,需要确认以下问题

“团队是否太大而无法在共享代码库上有效工作?”

“团队被其他团队封锁了吗?”

“微服务是否为我们带来了明确的商业价值?”

“我的业务是否成熟到可以使用微服务?”

“我们当前的架构是否限制了我们的通信开销?”

如果你的应用程序不需要分解成微服务,你不需要这个。 没有绝对必要将所有应用程序分解为微服务。


事件驱动架构 (EDA) Event-Driven Architecture (EDA)

事件驱动架构 (EDA) 是关于使用事件作为系统内通信的一种方式。

通常,利用消息代理异步发布和使用事件。 发布者不知道谁在消费事件,消费者也不知道彼此。

事件驱动架构只是一种实现系统内服务之间松散耦合的方法。

什么是事件?

事件是表示系统中状态变化的数据点。

它没有指定应该发生什么以及更改应该如何修改系统,它只通知系统特定的状态更改。

当用户进行操作时,他们会触发一个事件

事件驱动架构具有三个关键组件:

事件生产者:向路由器发布事件。

事件路由器:过滤事件并将其推送给消费者。

事件消费者:使用事件来反映系统的变化。

注意:图中的点代表系统中的不同事件。

有几种方法可以实现事件驱动架构

Sagas

Publish-Subscribe

Event Sourcing

Command and Query Responsibility Segregation (CQRS)

优点

生产者和消费者脱钩。

高度可扩展和分布式。

易于添加新消费者。

提高敏捷性。

事件驱动架构的一些挑战

保证交货  Guaranteed delivery.

错误处理很困难 Error handling is difficult

事件驱动系统通常很复杂。

恰好一次,按顺序处理事件 Exactly once, in-order processing of events

常见用例:

元数据和指标。 Metadata and metrics.

服务器和安全日志。 Server and security logs.

集成异构系统。

扇出和并行处理 Fanout and parallel processing.

广泛使用的用于实现事件驱动架构的技术

NATS

Apache Kafka

Amazon EventBridge

Amazon SNS

Google PubSub


事件溯源 Event Sourcing

不是仅将数据的当前状态存储在域中,而是使用仅附加存储来记录对该数据采取的完整系列操作。

存储充当记录系统,可用于具体化域对象。

这可以通过避免同步数据模型和业务领域的需要来简化复杂领域中的任务,同时提高性能、可伸缩性和响应能力。 它还可以为事务数据提供一致性,并维护可以启用补偿操作的完整审计跟踪和历史记录。

Event sourcing vs Event-Driven Architecture (EDA) 事件溯源与事件驱动架构 (EDA)

事件溯源似乎经常与事件驱动架构 (EDA) 混淆。

事件驱动架构是关于使用事件在服务边界之间进行通信。

通常,利用消息代理在其他边界内异步发布和使用事件。

事件溯源是关于将事件用作状态,这是一种不同的存储数据的方法。

我们不是存储当前状态,而是存储事件。 

此外,事件溯源实现事件驱动架构的几种模式之一

优点

非常适合实时数据报告。

非常适合故障安全,可以从事件存储中重构数据。

极其灵活,可以存储任何类型的消息。

为高合规性系统实现审计日志功能的首选方式。

缺点

需要极其高效的网络基础设施。

需要一种可靠的方式来控制消息格式,例如模式注册表。

不同的事件将包含不同的有效载荷。


命令和查询责任分离 (CQRS) Command and Query Responsibility Segregation (CQRS)

命令查询责任分离 (CQRS) 是一种架构模式,它将系统的操作划分为命令查询

CQRS

命令是一条指令,一条执行特定任务的指令。 它是一种改变某些东西的意图并且不返回值,仅指示成功或失败。

查询是对不会改变系统状态或引起任何副作用的信息的请求。

CQRS 的核心原则是命令和查询的分离。

它们在系统中扮演着根本不同的角色,将它们分开意味着每个角色都可以根据需要进行优化,分布式系统可以真正从中受益。

具有事件溯源的 CQRS CQRS with Event Sourcing

CQRS 模式通常与事件溯源模式一起使用。

基于 CQRS 的系统使用单独的读写数据模型,每个模型都针对相关任务量身定制,并且通常位于物理上独立的存储中。

当与事件溯源模式一起使用时,事件的存储是写入模型并且是官方信息源。 基于 CQRS 的系统的读取模型提供了数据的物化视图,通常是高度非规范化的视图。

优点

允许独立扩展读取和写入工作负载。
更容易扩展、优化和架构更改。
更接近松散耦合的业务逻辑。
应用程序在查询时可以避免复杂的连接。
系统行为之间的界限清晰。

缺点

更复杂的应用程序设计。
可能会出现消息失败或重复消息。
处理最终一致性是一个挑战。
加大系统维护力度。

CQRS 有用的一些场景用例

数据读取的性能必须与数据写入的性能分开进行微调。

该系统预计会随着时间的推移而发展,并且可能包含多个版本的模型,或者业务规则定期更改的地方。

与其他系统集成,尤其是与事件源相结合,其中一个子系统的临时故障不应影响其他系统的可用性。

更好的安全性以确保只有正确的域实体才对数据执行写入。


API网关

API 网关是一种 API 管理工具,位于客户端和一组后端服务之间。

它是系统的单一入口点,封装了内部系统架构并提供为每个客户端量身定制的 API。

它还具有其他职责,例如身份验证、监控、负载平衡、缓存、节流、日志记录等。

为什么需要 API 网关?

微服务提供的 API 粒度通常与客户需要的粒度不同。 微服务通常提供细粒度的 API,这意味着客户端需要与多个服务进行交互。 因此,API 网关可以为所有客户端提供一个单一的入口点,并提供一些额外的功能和更好的管理

API 网关的一些所需功能:

身份验证和授权

服务发现

反向代理

缓存

安全

重试和熔断

负载均衡

记录、追踪

API组成

速率限制和节流

版本控制

路由

IP白名单或黑名单

优点

封装API的内部结构。
提供 API 的集中视图。
简化客户端代码。
监视、分析、跟踪和其他此类功能。

缺点

可能的单点故障。
可能会影响性能。
如果缩放不当,可能会成为瓶颈。
配置可能具有挑战性。

后端前端 (BFF) 模式 Backend For Frontend (BFF) pattern

Backend For Frontend (BFF) 模式中,我们创建单独的后端服务以供特定的前端应用程序或接口使用。

当我们想要避免为多个接口定制单个后端时,此模式很有用。

此外,有时微服务返回到前端的数据输出格式不准确,或者未按前端的需要进行过滤。 为了解决这个问题,前端应该有一些逻辑来重新格式化数据,因此,我们可以使用 BFF 将一些逻辑转移到中间层。

后端前端 (BFF) 模式后端的主要功能是从适当的服务中获取所需的数据,格式化数据,并将其发送到前端

GraphQL 作为 (BFF) 表现得非常好
  • 什么时候使用这个模式?

    在以下情况下,我们应该考虑使用后端前端 (BFF) 模式:

    必须以显着的开发开销来维护共享或通用后端服务。
      
    我们希望针对特定客户的要求优化后端。
      
    对通用后端进行定制以适应多个接口。
    

广泛使用的网关技术

Amazon API Gateway

Apigee API Gateway

Azure API Gateway

Kong API Gateway


选择正确的 API 技术 RESTGraphQLgRPC

良好的 API 设计始终是任何系统的重要组成部分。 但选择正确的 API 技术也很重要。

什么是 API?

API 是一组用于构建和集成应用程序软件的定义和协议

它有时被称为信息提供者信息用户之间的合同,确定生产者需要的内容和消费者需要的内容。

换句话说,如果您想与计算机或系统交互以检索信息或执行功能,API 可以帮助您将您想要的内容传达给该系统,以便它可以理解并完成请求。

RESTful API

REST API(也称为 RESTful API)是一种应用程序编程接口,它符合 REST 架构风格的约束并允许与 RESTful Web 服务进行交互。

REST 代表 Representational State Transfer

REST API 中,基本单位是资源

  • RESTful API 的一些概念

    [约束条件]

    为了使 API 被视为 RESTful,它必须符合以下架构约束:
        
      统一接口:应该有一种与给定服务器交互的统一方式。
      
      客户端-服务器:通过 HTTP 管理的客户端-服务器架构。
      
      无状态:请求之间不应将客户端上下文存储在服务器上。
      
      可缓存:每个响应都应包括响应是否可缓存以及响应可以在客户端缓存多长时间。
      
      分层系统:应用程序架构需要由多个层组成。
      
      按需代码:返回可执行代码以支持应用程序的一部分。 (选修的)
    

    [HTTP 动词]

    HTTP 定义了一组请求方法来指示要对给定资源执行的所需操作。
    虽然它们也可以是名词,但这些请求方法有时被称为 HTTP 动词。 
    它们中的每一个都实现了不同的语义,但是它们中的一组共享一些共同的特征。
    
    
    常用的 HTTP 动词:
      
      GET:请求指定资源的表示。
    
      HEAD:响应与 GET 请求相同,但没有响应主体。
    
      POST:将实体提交到指定资源,通常会导致状态更改或对服务器产生副作用。
    
      PUT:用请求负载替换目标资源的所有当前表示。
    
      DELETE:删除指定的资源。
    
      PATCH:对资源应用部分修改
    

    [HTTP 响应代码]

      HTTP 响应状态代码指示特定 HTTP 请求是否已成功完成。
           
        1xx - 信息响应。
        2xx - 成功响应。
        3xx - 重定向响应。
        4xx - 客户端错误响应。
        5xx - 服务器错误响应。
    

优点

简单易懂。
灵活便携。
良好的缓存支持。
客户端和服务器是解耦的。

缺点

过度获取数据。
有时需要多次往返服务器。
  • 场景

REST API 几乎被广泛使用,并且是设计 API默认标准。 总体而言,REST API 非常灵活,几乎可以适用于所有场景。

  • 示例

对用户资源进行操作的 REST API 的示例用法

URI HTTP 动词描述
/users GET 获取所有用户
/users/{id} GET 通过id获取用户
/users POST 添加一个新用户
/users/{id} PATCH 通过 id 更新用户
/users/{id} DELETE 通过 id 删除用户

GraphQL

GraphQL 是一种用于 API 的查询语言和服务器端运行时,它优先为客户提供他们请求的数据,而不是更多。 它由 Facebook 开发,后来于 2015 年开源。

GraphQL旨在使 API 快速、灵活且对开发人员友好。

此外,GraphQL 为 API 维护者提供了在不影响现有查询的情况下添加或弃用字段的灵活性。 开发人员可以使用他们喜欢的任何方法构建 API,而 GraphQL 规范将确保它们以客户可预测的方式运行。

GraphQL 中,基本单位是查询

  • GraphQL 中的一些关键概念

    图式 Schema

      GraphQL 模式描述了客户端连接到 GraphQL 服务器后可以使用的功能。
    

    查询 Queries

      查询是客户端发出的请求。 它可以包含查询的字段和参数。 查询的操作类型也可以是突变,它提供了一种修改服务器端数据的方法。
    

    解析器 Resolvers

      解析器是为 GraphQL 查询生成响应的函数集合。 简单来说,解析器充当 GraphQL 查询处理程序。
    

优点

消除数据的过度获取。
强定义模式。
代码生成支持。
有效载荷优化。

缺点

将复杂性转移到服务器端。
缓存变得困难。
版本控制不明确。
N+1 问题。
  • 场景

    减少应用程序带宽使用,因为我们可以在单个查询中查询多个资源。

    复杂系统的快速原型制作。

    当我们使用类似图形的数据模型时。

more about GraphQL at graphql.org

gRPC

gRPC 是一种现代开源高性能远程过程调用 (RPC) 框架,可以在任何环境中运行。

它可以通过对负载平衡、跟踪、健康检查、身份验证等的可插入支持,有效地连接数据中心内和跨数据中心的服务。

  • gRPC 的一些关键概念

    协议缓冲区 Protocol buffers

    Protocol buffers 提供了一种语言和平台中立的可扩展机制,
    用于以向前和向后兼容的方式序列化结构化数据。 
    
    它类似于 JSON,只是它更小更快,并且它生成本地语言绑定。
    

    服务定义

    与许多 RPC 系统一样,gRPC 基于定义服务并指定可以使用其参数和返回类型远程调用的方法的思想。
    
    gRPC 使用协议缓冲区作为接口定义语言 (IDL) 来描述服务接口和有效负载消息的结构。
    

优点

轻巧高效。
高性能。
内置代码生成支持。
双向流。

缺点

与 REST 和 GraphQL 相比相对较新。
有限的浏览器支持。
更陡峭的学习曲线。
不是人类可读的。
  • 场景用例

    通过双向流进行实时通信。

    微服务中高效的服务间通信。

    低延迟和高吞吐量通信。

    多语言环境。

  • 示例

下面是在 *.proto 文件中定义的gRPC 服务的基本示例。

使用此定义,我们可以使用我们选择的编程语言轻松地生成 HelloService 服务。

service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string greeting = 1;
}

message HelloResponse {
  string reply = 1;
}

REST vs GraphQL vs gRPC

Type 耦合 Chattiness 性能
REST Low High Good
GraphQL Medium Low Good
gRPC High Medium Great


长轮询、WebSockets、服务器发送事件 (SSE) Long polling, WebSockets, Server-Sent Events (SSE)

Web 应用程序最初是围绕客户端-服务器模型开发的,其中 Web 客户端始终是事务的发起者,例如从服务器请求数据。

因此,没有任何机制可以让服务器在没有客户端先发出请求的情况下独立地向客户端发送或推送数据

长轮询 Long polling

HTTP 长轮询是一种用于尽快从服务器向客户端推送信息的技术。

结果,服务器不必等待客户端发送请求

在长轮询中,服务器一旦收到客户端的请求就不会关闭连接。 相反,服务器仅在有任何新消息可用或达到超时阈值时才响应。

  • 轮询如何工作

    客户端发出初始请求并等待响应。

    服务器收到请求并延迟发送任何内容,直到更新可用。

    一旦更新可用,响应就会发送到客户端。

    客户端收到响应并立即或在某个定义的时间间隔后发出新请求以再次建立连接

优点

易于实施,适合小型项目。
几乎得到普遍支持。

缺点

每次都创建一个新连接,这在服务器上可能很密集。
可靠的消息排序可能是多个请求的问题。
由于服务器需要等待新请求,延迟增加。

WebSockets

WebSocket 通过单个 TCP 连接提供全双工通信通道

它是客户端服务器之间的持久连接,双方可以使用它随时开始发送数据

客户端通过称为 WebSocket 握手的过程建立 WebSocket 连接。 如果该过程成功,则服务器和客户端可以随时双向交换数据。

WebSocket 协议使客户端和服务器之间的通信能够以较低的开销进行,从而促进与服务器之间的实时数据传输。

这是通过为服务器提供一种标准化的方式来向客户端发送内容而不被询问并允许在保持连接打开的同时来回传递消息来实现的。

  • WebSockets 如何工作

    客户端通过发送请求发起WebSocket握手过程。

    该请求还包含一个 HTTP 升级标头,允许请求切换到 WebSocket 协议 (ws://)。

    服务器向客户端发送响应,确认 WebSocket 握手请求。

    一旦客户端收到成功的握手响应,将打开 WebSocket 连接。

    现在客户端和服务器可以开始双向发送数据,允许实时通信。

    一旦服务器或客户端决定关闭连接,连接就会关闭

优点

全双工异步消息传递。
更好的基于来源的安全模型。
客户端和服务器的轻量级。

缺点

终止的连接不会自动恢复。
较旧的浏览器不支持 WebSockets(变得不那么相关)

服务器发送的事件 (SSE) Server-Sent Events (SSE)

Server-Sent Events (SSE) 是一种在客户端和服务器之间建立长期通信的方式, 使服务器能够主动向客户端推送数据。

它是单向的,这意味着一旦客户端发送请求,它只能接收响应而无法通过同一连接发送新请求

  • Server-Sent Events如何工作

    客户端向服务器发出请求。

    客户端和服务器之间的连接已建立并保持打开状态。

    当新数据可用时,服务器向客户端发送响应或事件

优点

易于为客户端和服务器实现和使用。
大多数浏览器都支持。
防火墙没有问题。

缺点

单向性可能会受到限制。
打开连接的最大数量限制。
不支持二进制数据。
Buy me a 肥仔水!