暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

90% 的故障,都栽在这 30 个系统设计点上……

本文转载自dbaplus社群

系统设计可能会让人感到不知所措,尤其是当你刚刚开始,并且不知道从哪里开始时。


但是一旦你理解了核心概念基本构成要素,它就会变得不再那么令人生畏——无论你是在准备面试,还是在工作中设计可扩展的系统


在本文中,我将向您介绍每个开发人员都应该知道的 30 个最重要的系统设计概念。



学习这些概念让我拿到了多家大厂的offer,在过去八年担任软件工程师期间,我见证了这些概念在构建和扩展大型系统时被反复运用。


一、客户端-服务器架构


您使用的几乎每个 Web 应用程序都是基于这个简单但功能强大的概念(称为客户端-服务器架构)构建的。



一方面,您有一个客户端——它可以是 Web 浏览器、移动应用程序或任何其他前端应用程序。


另一方面,您有一台服务器——一台持续运行、等待处理传入请求的机器。


客户端发送存储、检索或修改数据的请求。


服务器接收请求、处理请求、执行必要的操作并发回响应。


这听起来很简单,但有一个大问题:客户端如何知道在哪里找到服务器?


二、IP地址


客户端并不会神奇地知道服务器在哪里,它需要一个地址来定位并与其通信。


在互联网上,计算机使用IP 地址相互识别,IP 地址的作用就像服务器的电话号码一样。



每个公开部署的服务器都有一个唯一的 IP 地址。当客户端想要与服务交互时,必须向正确的 IP 地址发送请求。


但有一个问题:


  • 当我们访问一个网站时,我们不会输入它的 IP 地址——我们只需输入网站名称。

  • 我们不能指望用户(甚至系统)记住他们所连接的每个服务的一串随机数。

  • 如果我们将服务迁移到另一台服务器,其 IP 地址可能会发生变化——从而破坏所有直接连接。


三、DNS


我们不再依赖难以记住的 IP 地址,而是使用更加人性化的东西:域名。


但是,我们需要一种方法将域名映射到其对应的 IP 地址。


这就是DNS(或域名系统)的作用所在。它将易于记忆的域名(如algomaster.io)映射到其对应的 IP 地址。



以下是幕后发生的事情:


  • 当您algomaster.io在浏览器中输入内容时,您的计算机会向 DNS 服务器询问相应的 IP 地址。

  • 一旦 DNS 服务器响应 IP,您的浏览器就会使用它与服务器建立连接并发出请求。


您可以使用ping命令查找任何域名的 IP 地址。只需打开终端,输入 ping 后跟域名即可。它会返回当前分配给该域名的 IP 地址。


四、代理/反向代理


当您访问网站时,您的请求并不总是直接发送到服务器 - 有时,它会先通过代理反向代理


代理服务器充当您的设备和互联网之间的中间人



当您请求网页时,代理会将您的请求转发到目标服务器,检索响应,然后将其发送回给您。


代理隐藏您的 IP 地址,保证您的位置和身份的私密性。


反向代理则相反,它拦截客户端请求,并根据预定义的规则将其转发到后端服务器。



允许直接访问服务器可能会带来安全风险,使其面临黑客DDoS 攻击等威胁。


反向代理充当受控入口点,调节传入流量并隐藏服务器 IP,从而减轻这些风险。


它还可以充当负载平衡器,在多个服务器之间分配流量。


五、延迟


客户端与服务器通信时总会存在延迟,而造成延迟的最大原因之一就是物理距离。



例如,如果我们的服务器位于纽约,但印度的用户发送请求,则数据必须传输到地球的另一端,然后响应也必须经过同样漫长的旅程返回。


这种往返延迟称为延迟——数据在客户端和服务器之间传输所需的总时间。高延迟会使应用程序运行缓慢且响应迟钝。


减少延迟的一种方法是在全球多个数据中心部署我们的服务。


这样,用户就可以连接到最近的服务器,而不必等待数据在全球范围内传输。


一旦建立连接,客户端和服务器实际上如何通信?


六、HTTP/HTTPS


每次您访问网站时,您的浏览器和服务器都会使用一组称为HTTP(超文本传输协议)的规则进行通信。


这就是为什么大多数 URL 都以http://或其安全版本开头https://。



工作原理如下:


客户端向服务器发送请求。该请求包含一个标头(包含请求类型、浏览器类型和 Cookie 等详细信息),有时还包含一个请求正文(包含其他数据,例如表单输入)。


服务器处理请求并通过HTTP 响应进行响应 — 返回请求的数据,如果出现错误则返回错误消息。


HTTP 存在一个重大的安全漏洞,它以纯文本形式发送数据。这是一个严重的问题,尤其是对于密码、信用卡信息和个人数据等敏感信息。


这就是为什么现代网站改用HTTPS(超文本传输协议安全)的原因。HTTPS 使用SSL/TLS加密所有数据,确保即使有人拦截请求,也无法读取或更改数据。


但是客户端和服务器并不直接交换原始 HTTP 请求和响应。


HTTP 只是一个传输数据的协议,但它没有定义:


  • 请求应该如何组织

  • 回复应采用什么格式

  • 或者不同的客户端应该如何与服务器交互。


这就是API(应用程序编程接口)的用武之地。


七、API


可以将 API 视为一个中间人,它允许客户端(如 Web 和移动应用程序)与服务器进行通信,而无需担心低级细节。



您使用的几乎每项数字服务(社交媒体、电子商务、网上银行、叫车应用程序)都是建立在幕后协同工作的 API 之上的。


它通常的工作方式如下:


  • 客户向 API 发送请求。

  • 托管在服务器上的API处理请求、与数据库或其他服务交互并准备响应。

  • API以结构化格式(通常是JSONXML发回响应,客户端可以理解并显示该格式。


API 提供了一个抽象层——客户端不需要知道服务器如何处理请求,只需要知道它返回预期的数据。


八、Rest API


在不同的API风格中,REST(表述性状态转移)的使用最为广泛。


REST API遵循一组规则,这些规则定义了客户端和服务器如何以结构化的方式通过 HTTP 进行通信。



其余的是:


  • 无状态:每个请求都是独立的;服务器不存储客户端状态。

  • 基于资源:一切都被视为资源(例如,/用户,/订单,/产品)。

  • 使用标准 HTTP 方法:客户端使用HTTP 方法与资源交互,例如:

  • GET → 检索数据(例如,获取用户资料)。

  • POST → 创建新数据(例如,添加新用户)。

  • PUT/PATCH → 更新现有数据(例如,更改用户设置)。

  • 删除→ 删除数据(例如,删除帐户)。


REST API 很棒,因为它们简单、可扩展且易于缓存,但它们也有局限性,尤其是在处理复杂的数据检索时。


REST 端点通常会返回超出实际需求的数据,导致网络使用效率低下。如果 API 未返回相关数据,客户端可能需要发出多个请求才能检索所有所需信息。


为了应对这些挑战,Facebook 于 2015 年推出了 GraphQL。


九、GraphQL


与强制客户端检索固定数据集的REST 不同,GraphQL允许客户端精确地请求他们所需要的数据 — — 不多不少。


使用 REST API,如果您需要用户详细信息、用户个人资料详细信息及其最近的帖子,则可能必须向不同的端点发出多个请求:


  • GET api/users/123→ 获取用户详细信息

  • GET api/users/123/profile→ 获取用户资料

  • GET api/users/123/posts→ 获取用户的帖子


使用 GraphQL,您可以将这些请求合并为一个,并在单个查询中准确获取所需的数据:



服务器仅响应请求的字段,减少不必要的数据传输并提高效率。


然而,GraphQL 也存在一些缺点——它需要在服务器端进行更多处理,并且不像 REST 那样容易缓存。


当客户端发出请求时,他们通常想要存储或检索数据。


但这又引出了另一个问题——实际数据存储在哪里?


十、数据库


如果我们的应用程序处理少量数据,我们可以将其存储在内存中


但现代应用程序要处理大量数据— — 远远超出了内存的有效处理能力。


这就是为什么我们需要一个专门的服务器来存储和管理数据——数据库。


数据库是任何应用程序的支柱。 它确保数据被高效地存储、检索和管理,同时保证数据的安全性、一致性和持久性。


当客户端请求存储检索数据时,服务器会与数据库通信,获取所需的信息,并将其返回给客户端。



但并非所有数据库都相同。不同的应用程序对可扩展性、性能和一致性有不同的要求,因此选择正确的数据库类型至关重要。


在系统设计中,我们通常在SQL 和 NoSQL 数据库之间进行选择。


十一、SQL 与 NoSQL



SQL 数据库将数据存储在具有严格预定义模式的表中,并遵循ACID 属性。


  • 原子性——事务要么全部完成,要么全部不完成(要么全部完成)。

  • 一致性——数据始终保持有效并遵循定义的规则。

  • 隔离性——交易不会互相干扰。

  • 持久性——数据一旦保存,即使系统崩溃,也不会丢失。


由于这些保证,SQL 数据库非常适合需要强一致性和结构化关系的应用程序,例如银行系统


流行的 SQL 数据库示例包括:MySQL 和 PostgreSQL


另一方面,NoSQL 数据库旨在实现高可扩展性和性能。


它们不需要固定的模式并使用不同的数据模型,包括:


  • 键值存储——快速查找简单的键值对(例如 Redis)。

  • 文档存储——存储灵活的、类似 JSON 的文档(例如 MongoDB)。

  • 图形数据库——最适合高度连接的数据(例如 Neo4j)。

  • 宽列存储——针对大规模分布式数据(例如 Cassandra)进行了优化。


那么,您应该使用哪一个呢?这取决于系统要求。


  • 如果您需要具有强一致性的结构化关系数据→ SQL 是更好的选择。

  • 如果您需要高可扩展性、灵活的模式或大规模快速读/写→ NoSQL 是更好的选择。


许多现代应用程序同时使用 SQL 和 NoSQL。


例如,电子商务平台可能会:


  • 将客户订单存储在 SQL 中(因为它们需要严格的一致性)。

  • 并将产品推荐存储在 NoSQL 中(因为它们需要灵活而快速的查找)。


十二、垂直扩展


随着我们的用户群不断增长,访问我们应用服务器的请求数量也在不断增加。


最初,一台服务器可能足以处理负载。但是,随着流量的增加,这台服务器可能会成为瓶颈,拖慢整个系统的速度。


最快的解决方案之一是通过添加更多 CPU、RAM 或存储来升级现有服务器。



这种方法称为垂直扩展(向上扩展) ——使单台机器更加强大。


但这种方法存在一些主要局限性:


  • 硬件限制 →你不可能永远升级服务器。每台机器都有最大容量。

  • 成本→功能更强大的服务器会变得越来越昂贵。

  • 单点故障(SPOF) →如果这台服务器崩溃,整个系统就会崩溃。


因此,虽然垂直扩展是一种快速解决方案,但它并不是处理高流量和确保系统可靠性的长期解决方案。


让我们看看一个更好的方法——一个让我们的系统更具可扩展性和容错性的方法。


十三、水平扩展


如果我们不升级单个服务器,而是添加更多服务器来分担负载会怎么样?



这种方法称为水平扩展(扩展) ——我们将工作负载分布到多台机器上。


这种方法更好,因为:


  • 更多服务器 = 更多容量 →系统可以更有效地处理不断增加的流量。

  • 无单点故障→如果一台服务器出现故障,其他服务器可以接管,从而提高可靠性。

  • 经济高效→我们可以使用多台价格实惠的机器,而不必投资购买一台超级昂贵的机器。


但水平扩展带来了一个新的挑战:客户端如何知道要连接哪个服务器?


这就是负载均衡器发挥作用的地方。


十四、负载均衡器



负载均衡器位于客户端和后端服务器之间,充当在多个服务器之间分配请求的流量管理器。


如果一台服务器崩溃,负载均衡器会自动将流量重定向到另一台健康的服务器。


但是负载均衡器如何决定哪个服务器应该处理下一个请求?


它使用负载平衡算法,例如:


  • 循环 →请求按顺序循环发送到服务器。

  • 最少连接 →请求被发送到具有最少活动连接的服务器。

  • 和 IP 哈希 →来自同一 IP 地址的请求总是发送到同一台服务器,这有助于会话一致性


到目前为止,我们已经讨论了扩展我们的应用程序服务器,但随着流量的增长,数据量也会增加。


首先,我们可以通过添加更多 CPU、RAM 和存储来垂直扩展数据库,但单台机器的处理能力是有限的。


因此,让我们探索其他有助于有效管理大量数据的数据库扩展技术。


十五、数据库索引


加快数据库读取查询的最快和最有效的方法之一是索引。


可以将其想象成书后面的索引页——无需翻阅每一页,而是直接跳到相关部分。


数据库索引的工作原理相同。它是一种超高效的查找表,可帮助数据库快速找到所需数据,而无需扫描整个表。



索引存储列值以及指向表中实际数据行的指针。


索引通常在经常查询的列上创建,例如:


  • 主键

  • 外键

  • WHERE 条件中使用的列


但要小心——虽然索引可以加快读取速度,但它们会减慢写入速度(INSERT,, ) UPDATE,DELETE因为每当数据发生变化时都需要更新索引。


这就是为什么我们应该只索引最常访问的列。


索引可以显著提高读取性能,但如果索引还不够,我们的数据库无法处理越来越多的读取请求,该怎么办?


这就是我们的下一个数据库扩展技术复制的用武之地。


十六、复制


就像我们添加更多应用服务器来处理流量一样,我们可以通过在多台服务器上创建数据库的副本来扩展数据库。



工作原理如下:


  • 我们有一个主数据库(也称为主副本)来处理所有写入操作(INSERT,,)UPDATE。DELETE

  • 我们有多个处理读取查询(SELECT)的读取副本。

  • 每当数据写入主数据库时,它都会被复制到只读副本,以便它们保持同步。


复制提高了读取性能,因为读取请求分散在多个副本上,从而减少了每个副本的负载。


这也提高了可用性,因为如果主副本发生故障,则只读副本可以接管为新的主副本。


复制对于扩展读取密集型应用程序非常有用,但如果我们需要扩展写入或存储大量数据怎么办?


十七、分片


假设我们的服务现在有数百万用户,并且我们的数据库已经增长到数 TB 的数据。


单个数据库服务器最终将难以有效地处理所有这些数据。


我们不是将所有内容都放在一个地方,而是将数据库拆分成更小、更易于管理的部分,并将它们分布在多台服务器上。



这种技术被称为分片


  • 我们将数据库划分为称为分片的较小部分。

  • 每个分片包含总数据的一个子集。

  • 数据根据分片键(例如用户 ID)进行分布。


通过这种方式分发数据,我们:


  • 减少数据库负载→每个分片仅处理一部分查询。

  • 加快读写性能→查询分布在多个分片上,而不是命中单个数据库。


分片也称为水平分区,因为它按行分割数据。


但如果问题不在于行数,而在于列数,该怎么办?


在这种情况下,我们使用垂直分区,按列拆分数据库。接下来我们来探讨一下。


十八、垂直分区


假设我们有一个用户表,其中存储:


  • 个人资料详情(姓名、电子邮件、个人资料图片)

  • 登录历史记录(上次登录、IP 地址)

  • 和账单信息(账单地址、付款详情)


随着该表的增长,查询变得越来越慢,因为即使请求只需要几个特定的字段,数据库也必须扫描许多列。


为了优化这一点,我们使用垂直分区,根据使用模式将用户表分成更小、更集中的表。



  • User_Profile → 存储姓名、电子邮件、个人资料图片。

  • User_Login → 存储登录时间戳。

  • User_Billing → 存储账单地址、付款详情。


这提高了查询性能,因为每个请求仅扫描相关列而不是整个表。


它减少了不必要的磁盘 I/O,使数据检索更快。


然而,无论我们如何优化数据库,从磁盘检索数据总是比从内存检索慢。


如果我们可以将经常访问的数据存储在内存中以实现闪电般的快速访问,那会怎样?


这称为缓存。


十九、缓存


缓存用于通过将经常访问的数据存储在内存中而不是重复从数据库中获取来优化系统性能。


最常见的缓存策略之一是缓存旁路模式。



工作原理如下:


  • 当用户请求数据时,应用程序首先检查缓存。

  • 如果数据缓存中,则会立即返回,避免数据库调用。

  • 如果数据不在缓存中,应用程序将从数据库中检索数据,将其存储在缓存中以供将来的请求使用,然后将其返回给用户。

  • 下次请求相同的数据时,将直接从缓存中提供数据,从而使请求速度更快。


为了防止提供过时的数据,我们使用生存时间(TTL) ——在缓存数据上设置的过期时间,以便它在一定时间后自动刷新。


流行的缓存工具包括Redis 和 Memcached


让我们看看下一个数据库扩展技术。


二十、非规范化


大多数关系数据库使用规范化将数据分成单独的表来有效地存储数据。


例如,在电子商务系统中:


  • 用户表存储用户详细信息。

  • 订单表存储了他们的订单。

  • 产品表存储产品详细信息。


虽然这减少了冗余,但也引入了连接。从多个表检索数据时,数据库必须使用 JOIN 操作将它们组合起来,这会随着数据集的增长而降低查询速度。


    SELECT o.order_id, u.name, u.email, o.product, o.amount
    FROM orders o
    JOIN users u ON o.user_id = u.user_id;


    非规范化通过将相关数据合并到单个表中来减少连接的数量,即使这意味着某些数据会被重复。



    示例:我们不是将用户详细信息Users 及其最新订单保存在单独的表中,而是创建表来存储用户详细信息及其最新订单。 OrdersUserOrders


    现在,在检索用户的订单历史记录时,我们不需要JOIN 操作——数据已经存储在一起,从而实现更快的查询和更好的读取性能。


      SELECT order_id, user_name AS name, user_email AS email, product, amount
      FROM orders;


      非规范化通常用于读取密集型应用程序,其中速度更为关键,但缺点是它会导致存储使用量的增加和更复杂的更新请求。


      二十一、CAP定理


      当我们将系统扩展到多个服务器、数据库和数据中心时,我们就进入了分布式系统的世界。


      分布式系统的基本原理之一是CAP 定理,该定理指出:没有任何分布式系统可以同时实现以下三项:



      • 一致性(C)→每个节点始终返回最新的数据。

      • 可用性(A) →系统始终响应请求,即使某些节点发生故障(但数据可能不是最新的)。

      • 分区容错性(P) →即使节点之间出现网络故障,系统仍能继续运行。


      由于网络故障(P)不可避免,我们必须在以下两者之间进行选择:


      • 一致性 + 分区容错性 (CP) → 确保每个请求都获取最新数据,但在发生故障时可能会拒绝请求。例如:MySQL 等 SQL 数据库。

      • 可用性 + 分区容错性 (AP) → 确保系统始终响应,即使某些数据已过时。示例:Cassandra 和 DynamoDB 等 NoSQL 数据库。


      在分布式 NoSQL 数据库中,实现所有服务器的即时一致性太慢。


      相反,我们使用最终一致性——这意味着:


      • 并非所有节点都会立即更新,但只要有足够的时间,它们最终会同步并返回相同的数据。

      • 这使得系统即使在极端负载下也能保持高可用性和快速性。


      最终一致性的工作原理:


      • 用户更新数据库的一个副本中的数据。

      • 系统立即确认更新,确保高可用性。

      • 然后将更新异步传播到其他副本。

      • 短暂的延迟后,所有副本都具有最新数据,从而确保了长期的一致性。


      二十二、Blob存储


      大多数现代应用程序不仅仅存储文本记录,还需要处理图像、视频、pdf和其他大型文件


      但问题在于:传统数据库并非设计用于高效存储大型非结构化文件。


      那么,解决方案是什么?


      我们使用像 Amazon S3 这样的 Blob 存储——一种在云中存储大型非结构化文件的高度可扩展且经济高效的方式。



      Blob 是单独的文件,例如图像、视频或文档。


      这些 blob 存储在云中的逻辑容器或存储桶内。


      每个文件都有一个唯一的 URL,从而可以轻松地通过网络检索和提供服务。


      示例:https://my-bucket-name.s3.amazonaws.com/videos/tutorial.mp4


      使用 Blob 存储有几个优点,例如:


      • 可扩展性→它可以轻松存储PB级的数据。

      • 按使用量付费定价→您只需为实际使用的存储和检索付费。

      • 自动复制→数据跨多个数据中心和可用区域进行复制,以确保持久性。

      • 轻松访问→可以使用 REST API 或直接 URL 检索文件。


      一个常见的用例是将音频或视频文件实时传输到用户应用程序。


      但是直接从 blob 存储进行流式传输可能会很慢,特别是当数据存储在较远的位置时。


      二十三、CDN


      例如,假设您在印度尝试观看托管在加利福尼亚服务器上的 YouTube 视频。


      由于视频数据必须传输到世界各地,这可能会导致缓冲和加载时间缓慢。


      内容分发网络(CDN)通过根据用户位置更快地向用户分发内容来解决此问题。



      来源:https ://www.cloudflare.com/learning/cdn/what-is-a-cdn/


      CDN 是一个由分布式服务器组成的全球网络,这些服务器协同工作,根据用户的地理位置向用户提供网络内容(如 HTML 页面、JavaScript 文件、样式表、图像和视频) 。


      CDN不是从单个数据中心提供内容,而是将静态内容缓存在位于世界各地的多个边缘服务器上。


      当用户请求内容时,最近的 CDN 服务器会传送内容,而不是一路到达原始服务器。


      由于内容是由最近的 CDN 节点提供的,因此用户可以体验到更快的加载时间和最少的缓冲。



      二十四、WebSockets


      大多数 Web 应用程序使用HTTP,它遵循请求-响应模型。


      • 客户端发送请求。

      • 服务器处理请求并发送响应。

      • 如果客户端需要新数据,则必须发送另一个请求。


      这对于静态网页来说效果很好,但对于实时应用程序(如实时聊天应用程序、股票市场仪表板和在线多人游戏)来说太慢且效率低下。


      对于 HTTP,获取实时更新的唯一方法是通过轮询——每隔几秒发送重复请求。


      但是轮询效率低下,因为它会增加服务器负载并浪费带宽,因为大多数响应都是空的(当没有新数据时)。


      WebSockets 通过允许客户端和服务器通过单个持久连接进行持续的双向通信来解决此问题。



      WebSockets 的工作原理如下:


      • 客户端发起与服务器的WebSocket 连接。

      • 一旦建立,连接就会保持打开状态。

      • 服务器可以随时向客户端推送更新,而无需等待请求。

      • 客户端还可以立即向服务器发送消息。


      这使得实时交互成为可能,并且无需轮询。


      WebSockets 支持客户端和服务器之间的实时通信,但是如果服务器需要在事件发生时通知另一台服务器怎么办?


      例子:


      • 当用户付款时,Stripe 需要立即通知您的应用程序。

      • 如果有人将代码推送到 GitHub,则应自动触发 CI/CD 系统(例如 Jenkins)。


      输入 Webhooks。


      二十五、Webhook


      Webhooks允许服务器在事件发生时立即向另一台服务器发送 HTTP 请求,而不是不断轮询 API来检查事件是否发生。



      工作原理如下:


      • 接收者(您的应用程序)向提供商(例如 Stripe、GitHub、Twilio)注册一个 webhook URL 。

      • 当发生事件时(例如,用户付款),提供商会向 webhook URL 发送包含事件详细信息的 HTTP POST 请求。

      • 您的应用程序处理传入的请求并相应地更新数据。


      这节省了服务器资源并减少了不必要的 API 调用。


      二十六、微服务


      传统上,应用程序是使用单片架构构建的,其中:


      • 所有功能(例如身份验证、付款、订单、运输)都位于一个大型代码库中。

      • 如果系统的一部分出现故障或需要扩展,整个系统都会受到影响。

      • 部署是有风险的——一次错误的更新可能会导致整个应用程序崩溃。


      示例:想象一个电子商务应用程序,其中订单、付款、库存和运输模块都紧密连接到单个代码库中。


      如果库存系统崩溃,整个应用程序可能会崩溃。


      整体式架构对于小型应用程序来说运行良好,但对于大型系统来说,它们变得难以管理、扩展和部署。


      解决方案是将您的应用程序分解为更小、更独立、可以协同工作的服务(称为微服务) 。



      每个微服务:


      • 处理单一职责

      • 有自己的数据库和逻辑,因此可以独立扩展。

      • 使用API 或消息队列与其他微服务通信。


      这样,服务可以单独扩展和部署,而不会影响整个系统。


      然而,当多个微服务需要通信时,直接API 调用并不总是有效的——这就是消息队列的用武之地。


      二十七、消息队列


      在单片系统中,函数直接相互调用并等待响应。


      但在基于微服务的系统中,这种方法效率低下,因为:


      如果一个服务很慢或者宕机,所有的服务都会等待。

      高流量可能会使单个服务超负荷。

      同步通信(等待立即响应)的扩展性不佳。


      消息队列使服务能够异步通信,从而允许处理请求而不阻塞其他操作。



      工作原理如下:


      • 生产者(例如结账服务)将消息放入队列(例如“处理付款”)。

      • 队列暂时保存消息,直到消费者(例如支付服务)准备好处理它。

      • 消费者检索消息并进行处理。


      使用消息队列,我们可以解耦服务并提高可扩展性和容错能力。


      常见的消息队列系统包括:Apache Kafka、RabbitMQ等。


      使用消息队列,我们可以防止系统内部服务过载。


      但是,我们如何防止我们部署的公共 API 和服务过载。


      我们使用速率限制。


      二十八、速率限制


      想象一下,一个机器人开始每秒向您的网站发出数千个请求。


      如果没有限制,这可以:


      • 消耗所有可用资源,导致服务器崩溃。

      • 由于过度使用 API 而增加云成本。

      • 并降低合法用户的性能。


      速率限制限制了客户端在特定时间范围内可以发送的请求数量。



      工作原理如下:


      每个用户或 IP 地址都分配有一个请求配额(例如,每分钟 100 个请求)。

      如果超过此限制,服务器将暂时阻止其他请求返回错误(HTTP 429 - 请求过多)。


      速率限制算法有很多种,其中比较流行的有:


      • 固定窗口→根据固定时间窗口限制请求(例如,每分钟 100 个请求)。

      • 滑动窗口→更灵活的版本,可动态调整限制以平滑请求突发。

      • 令牌桶→用户获取请求的令牌,令牌会随着时间的推移以固定的速率补充


      我们不需要实现自己的速率限制系统——这可以通过称为API 网关的东西来处理。


      二十九、API 网关


      API 网关是一种集中式服务,用于处理身份验证、速率限制、日志记录和监控以及请求路由。


      想象一个具有多种服务的基于微服务的应用程序。


      API 网关并不直接公开每个服务,而是充当所有客户端请求的单一入口点。



      API 网关的工作原理:


      • 客户端向 API 网关发送请求。

      • 网关验证请求(例如,身份验证、速率限制)。

      • 它将请求路由到适当的微服务。

      • 响应通过网关发送回客户端。


      API网关简化了API管理,提高了可扩展性和安全性。


      流行的 API 网关解决方案包括NGINX、Kong 和 AWS API 网关。


      三十、幂等性


      在分布式系统中,网络故障服务重试很常见。如果用户意外刷新支付页面,系统可能会收到两个支付请求,而不是一个。



      幂等性确保重复的请求产生的结果与仅发出一次请求时相同的结果。


      工作原理如下:


      • 每个请求都分配有一个唯一的 ID(例如request_1234)。

      • 在处理之前,系统会检查该请求是否已被处理

      • 如果是 → 它将忽略重复的请求。

      • 如果没有 → 它正常处理请求。


      幂等性可以防止重复交易并确保分布式系统中的数据一致性。


      作者丨Ashish Pratap Singh  编译丨Rio
      来源丨网址:https://medium.com/algomaster-io/system-design-was-hard-until-i-learned-these-30-concepts-78042ff99cae
      dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn

      文章转载自杨建荣的学习笔记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

      评论