首页 › AI Coding › System Design › 系统设计精要 - 2

系统设计精要 - 2

21. 事件驱动架构​

事件驱动架构(Event-Driven Architecture, EDA)是一种以事件为媒介、实现服务与应用程序之间异步通信的架构范式。在同步通信模型中,调用方服务必须阻塞等待被调方返回响应后方可继续执行,这导致服务之间形成强耦合关系;一旦某一服务响应延迟或发生故障,整个调用链路的可用性都将受到连带影响。​

​

事件驱动架构

事件驱动架构​

​

在事件驱动架构中,服务之间不再进行直接调用。取而代之的是,服务在触发事件后立即返回,无需等待其他服务响应;其余服务通过订阅并监听该事件,进而执行相应处理逻辑。​

•

服务A → 发布事件 → 向用户返回响应​

•

服务B → 订阅事件并作出响应​

核心构建块​

•

事件(Event):对某一状态变更或行为发生的消息化描述,例如订单已创建、用户已注册、订单已送达。​

•

生产者(Producer):负责发布事件的服务。​

•

消费者(Consumer):负责订阅并处理事件的服务。​

•

事件代理 / 消息队列(Event Broker / Message Queue):负责暂存消息的中间件,接收生产者发布的消息并将其投递给消费者。​

优势​

•

服务之间松耦合​

•

消费者可独立横向扩展​

•

异步处理​

•

新增消费者成本低​

​

​

22. 无服务器架构​

单体架构与微服务架构均运行在由团队自行管理的服务器之上,无服务器架构则不同。该命名具有一定误导性:服务器依然存在,只是资源的配置、扩缩容、补丁更新与关停等运维工作均由云服务商代为承担。​

​

无服务器架构

无服务器架构​

​

开发者只需编写函数并将其部署,当请求到达时平台会自动运行该函数,执行完毕后即释放资源。无服务器计算本质上是一种按需分配的服务器使用模式,而非真正意义上“没有服务器”。​

•

事件驱动:请求到达时触发函数执行(即事件触发)。​

•

自动伸缩:若并发请求数为1000,平台将启动同等数量的函数实例。​

•

冷启动:由于每次处理请求均需新建运行环境,因此存在一定处理延迟,该现象称为冷启动。​

•

无状态:各函数实例相互独立,不共享任何状态。​

•

按使用量计费:依据实际资源消耗量进行计费。​

适用场景​

•

事件驱动型任务​

•

流量稀疏或不可预测的场景​

•

需要快速迭代开发的场景​

​

​

23. 内容分发网络(CDN)​

CDN(Content Delivery Network,内容分发网络)是由分布于全球各地的服务器节点构成的网络体系,通过将内容缓存并分发至更靠近用户的位置,避免请求回传至源站。​

​

内容分发网络​

​

工作原理​

•

源站是存放所有真实内容的服务器。​

•

内容被缓存至遍布全球的边缘服务器。​

•

诸如 CSS、JS、HTML 与图片等静态资源会被缓存至边缘节点,并由边缘节点直接提供服务。​

•

每个用户请求均被路由至最近的边缘服务器。​

•

缓存命中 → 立即返回响应。​

•

缓存未命中 → 从源站拉取内容,在边缘节点生成副本,并返回响应。​

CDN 最适用于静态内容或更新频率较低的内容,不适用于实时性要求高、高度动态的数据,例如实时银行余额。​

​

​

24. 消息队列​

消息队列是位于生产者与消费者之间的缓冲层,用于暂存消息直至其被处理,从而使生产者无需等待消费者就绪。​

生产者 → 发送消息 → 队列 → 消费者在就绪时取出消息​

​

消息队列​

​

为何不直接调用消费者?​

在生产者直接调用消费者的模型中,消费者必须始终处于运行状态、响应及时且持续可用。一旦消费者宕机或处于恢复状态,调用将失败,进而导致请求失败。​

队列位于生产者与消费者之间:​

生产者 → 队列 → 消费者​

生产者无需关心消费者是否响应缓慢或已宕机,只需将消息推送至队列即可完成自身职责。消费者可在自身节奏下获取消息并执行处理。若消费者发生崩溃,消息仍会在队列中持久化等待,不会丢失。​

实际案例​

•

订单服务 → 将“SendInvoice”消息推送至队列​

•

订单服务 → 立即向客户端返回响应​

•

发票处理程序 → 从队列拉取消息 → 生成发票​

生产者立即继续后续流程,消费者按自身节奏处理。​

关键特性​

•

解耦:生产者与消费者之间无需相互感知。​

•

缓冲:可吸收流量峰值;消费者按自身处理能力消费。​

•

可靠性:消息在队列中持久化,直至被成功处理。​

•

原生异步:生产者从不阻塞等待响应。​

消息队列是事件驱动架构的重要组成部分。​

​

​

25. 发布-订阅​

消息队列采用单生产者、单消费者模型处理每条消息。而发布/订阅模式解决的是另一类问题:当多个服务均需对同一事件作出响应时,应如何处理?​

​

发布-订阅​

​

发布者向某一主题发布消息,所有订阅该主题的订阅者均会收到一份消息副本。与队列中每条消息仅投递给一个消费者不同,发布/订阅模式会将每条消息广播至所有订阅者。​

发布者既不感知也不关心订阅者的身份,仅负责执行发布动作。​

队列与发布/订阅的对比​

•

队列:一条消息由一个消费者消费。​

•

发布/订阅:一条消息被分发至每一个订阅者。​

​

​

26. 后台处理作业​

设想用户下单购买了一件商品。您正在设计一项功能,当订单提交后,系统应当:​

•

向用户发送确认信息​

•

生成订单发票 PDF 并发送邮件​

​

​

后台任务处理​

​

•

向其手机号码发送包含订单号与预计送达日期的短信​

•

将数据同步至分析系统​

若将上述所有任务均置于同一个 API 调用(即创建订单 API)中同步执行,用户将不得不等待更长时间才能收到响应。在流量高峰期,系统响应时间可能进一步延长,甚至导致请求失败。​

这正是后台任务处理所解决的问题。​

与其以同步方式处理所有任务,不如将非关键任务迁移至后台延后处理,优先完成主请求。主请求首先被处理,所有关键操作在此期间执行完毕,随后返回响应;非关键性操作则在稍后异步执行。​

核心构建块​

•

作业 / 任务(Job / Task):需要执行的工作单元,例如“发送这封邮件”“生成这份报告”。​

•

作业队列(Job Queue):作业等待被空闲工作者处理的场所,如 Redis、RabbitMQ、SQS、BullMQ。​

•

工作进程(Worker):独立进程,负责从队列中获取任务并执行。​

后台处理作业模式​

•

异步或基于事件​

•

计划任务或 Cron 作业​

•

重复执行的作业​

•

延迟作业​

​

​

27. 定时任务​

定时任务是指在固定时间点或按重复计划自动运行的任务,无需任何用户或请求触发。​

​

Cron作业​

​

•

常规请求:客户端 → 发送请求 → 服务器响应​

•

Cron 作业:时钟到达预定时间 → 作业被触发​

Cron 作业通过特定语法进行调度:​

​

代码块​

Plain Text 自动换行复制

[ * * * * * ]​

​

•

0 0 * * * → 每天午夜执行​

•

0 */6 * * * → 每6小时执行一次​

使用场景​

•

每日数据库备份​

•

每日报表生成​

•

周期性数据清理​

•

每6小时发送邮件告警​

​

​

28. 反向代理​

反向代理部署于服务器前端,用于将服务器对客户端隐藏。客户端不再直接与真实服务器通信,而是先与反向代理建立连接。反向代理在内部将请求转发至相应服务器,获取响应后再返回给客户端。从客户端视角来看,代理即服务器,客户端无法感知其背后的真实拓扑。​

​

反向代理​

​

•

无反向代理时:客户端 → 服务器(客户端直接访问服务器)​

•

有反向代理时:客户端 → 反向代理 → 服务器(客户端仅感知代理)​

使用场景​

•

负载均衡、SSL 终止、缓存、安全防护、请求路由、响应压缩​

​

​

29. 正向代理​

正向代理部署于客户端与互联网之间,代表客户端发起请求。客户端不再直接与服务器通信,而是先与代理服务器交互。代理服务器代为转发请求并接收响应。从服务器视角来看,请求来源于代理而非客户端,服务器无法获知真实客户端身份。​

​

正向代理​

​

使用场景​

•

隐私保护、内容过滤、缓存、绕过地理限制、日志记录与监控​

​

正向代理 → 位于客户端之前,使服务器无法直接感知客户端。​

​

​

​

30. 身份验证​

身份验证是指核实用户身份的过程。以一个员工管理应用程序为例,任何用户若要使用该应用,均须输入用户名与密码。​

​

身份验证​

​

用户 → 用户名 + 密码 → 服务器验证凭据 → 身份确认 → 用户登录​

此时服务器已确认用户身份,这一过程即身份验证。​

常用身份验证方式​

•

用户名 + 密码、一次性密码(OTP)、生物识别、API 密钥、OAuth、JWT、基于会话的身份验证​

​

​

31. 授权​

​

授权​

​

授权是指验证用户是否具备执行某项操作的足够权限的过程。用户可能已通过身份认证,但尚未被授予对特定资源或操作的访问权限。​

例如,一个员工管理应用程序包含两类用户:​

管理员​

•

添加员工​

•

删除员工​

•

更新员工信息​

•

查看薪资信息​

员工​

•

查看个人档案​

•

更新个人资料​

•

查看已分配任务​

设想一名员工试图删除另一名员工:该用户已通过身份验证,但未被授权执行该操作。​

身份验证与授权的区别​

•

身份验证 = 你是谁?​

•

授权 = 你被允许做什么?​

​

​

32. JWT - JSON Web Token​

JWT(JSON Web Token)是一种用于身份验证与授权的令牌机制。它使服务器无需查询数据库即可验证用户身份,因为令牌本身携带了数据,并附带签名以证明其未被篡改。​

​

JSON Web 令牌​

​

JWT 的组成部分​

标头(Header)​

包含用于对令牌进行签名的算法,例如 HS256 或 RS256。​

载荷(Payload)​

包含实际数据,称为声明(Claims),例如 userId、角色与过期时间。​

签名(Signature)​

通过使用密钥对头部与载荷进行签名生成,用于证明令牌未被篡改。​

工作原理​

登录 → 服务器生成 JWT 并使用密钥签名,随后发送给客户端;客户端存储该令牌,并在每次请求的 Authorization 头中携带;服务器验证签名,若签名有效,则信任令牌中的数据,无需查询数据库。​

​

​

33. OAuth​

OAuth 是一种允许用户使用现有账号(如 Google 或 GitHub)进行登录或授权访问的机制,无需向应用提供密码。​

​

​

它解决的问题​

在没有 OAuth 的情况下,若希望用户通过 Google 登录,则必须让 Google 以某种方式将用户密码共享给应用。这显然是不可接受的,因为应用将因此具备直接登录用户真实 Google 账户的能力。​

OAuth 完全规避了这一问题:应用永远不会接触到用户密码。​

OAuth 的本质​

OAuth 是一种授权框架,用户可在不共享凭据的前提下,授予应用对其在其他服务上数据的有限访问权限。应用将获得一个令牌,其权限范围仅限于应用所需内容。​

OAuth 流程​

用户点击“使用 Google 登录”​

应用将用户重定向至 Google 登录页​

用户直接在 Google 页面登录,密码仅输入至 Google,而非应用​

Google 请求用户授权,例如“此应用需要您的电子邮件和个人资料”​

用户同意授权​

Google 重定向回应用,并附带授权码​

应用使用授权码换取访问令牌​

应用使用访问令牌从 Google 获取用户信息​

​

​

34. Cookie、本地存储与会话存储​

客户端存储允许浏览器保存少量数据,从而使应用无需每次均从服务器获取全部内容。以下是三种常见方案。​

​

Local Storage vs Session Storage vs Cookies​

​

Cookie​

小块数据,会在每次向服务器发送 HTTP 请求时自动随请求携带。​

本地存储(Local Storage)​

在浏览器中存储数据,即使关闭并重新打开浏览器后仍会保留。​

会话存储(Session Storage)​

与本地存储机制相同,但数据仅在该浏览器标签页的会话期间有效。​

​

​

35. 负载均衡​

​

​

设想您正在构建一个电子商务应用,并计划在午夜举行大型促销活动。突然间,成千上万的用户开始访问应用,单台服务器收到的请求数量超出其处理能力。此时应如何应对不断增长的流量?​

答案是增加更多服务器。​

但随之而来的新问题是:如何决定将哪个请求发送至哪台服务器?​

这正是负载均衡所解决的问题。​

负载均衡是指将传入请求分发至多台服务器的过程。负载均衡器部署于服务器前端,负责决定每个传入请求应被转发至哪台服务器,从而避免某台服务器因请求过多而过载,同时确保其他服务器不会处于空闲状态。请求的转发目标取决于所采用的负载均衡算法。​

​

​

36. 负载均衡算法 - 轮询​

​

负载均衡算法 - 轮询​

​

请求按顺序在各服务器间依次分配。当分配至最后一台服务器后,循环重新开始。​

•

请求1 → 服务器A​

•

请求2 → 服务器B​

•

请求3 → 服务器C​

•

请求4 → 服务器D​

•

请求5 → 服务器A​

•

请求6 → 服务器B​

​

​

37. 负载均衡算法 - 最少响应时间​

​

负载均衡算法 - 最小响应时间​

​

请求被发送至活动连接数最少的服务器。​

•

服务器A → 10个连接​

•

服务器B → 3个连接 ← 下一个请求​

•

服务器C → 9个连接​

该算法可能优于轮询法,因为并非所有请求的完成时间均相同。​

​

​

38. 负载均衡算法 - 最少连接数​

​

负载均衡算法——最少连接​

​

请求根据服务器性能进行路由。负载均衡器会综合考虑以下因素:​

•

当前活动连接数​

•

最近的响应时间​

请求被发送至响应更快、负载更轻的服务器。​

​

​

39. 负载均衡算法 - 加权轮询​

​

负载均衡算法 - 加权轮询​

​

部分服务器可能具备更强的处理能力。权重机制使性能更强的服务器能够接收更多流量。​

•

服务器A → 权重3​

•

服务器B → 权重1​

每个周期内:​

•

服务器A → 3个请求​

•

服务器B → 1个请求​

​

​

40. 负载均衡算法 - IP 哈希​

​

负载均衡算法 - IP哈希​

​

客户端的 IP 地址用于确定由哪台服务器接收请求。​

客户端IP →(哈希函数)→ 服务器B​

同一客户端通常会被路由至同一台服务器,这有助于实现会话持久化(粘性会话)。​