ARTICLE · 1025270
译文:每个开发者都应该掌握的 12 个软件架构基础概念

原文地址:https://medium.com/codex/12-architecture-concepts-every-developer-should-know-%EF%B8%8F-%EF%B8%8F-4ca41e032d4b
原文作者: Hemanth Raju Koneti

大多数开发者都是从学习如何编写能够正常运行的代码开始的。
这是第一个里程碑。你编写函数、构建 API、连接数据库、创建界面并交付功能。在这个阶段,成功意味着用户点击按钮或发送请求时,应用能够正确响应。
但随着系统不断发展,仅仅保证正确性已经不够了。
一个功能可能在面对十名用户时运行得无比顺畅,却在用户达到一万名时彻底崩溃。一个 API 单独运行时可能表现正常,却会在某个依赖项发生故障时变得不可靠。数据库今天还能快速响应查询,明天却可能成为瓶颈。后端代码可能写得干净整洁,但仍会因为流量路由不合理、缺少缓存或服务之间耦合过紧而发生故障。
架构正是从这里开始的。
软件架构并不是画一些华丽的图表,而是理解那些能够让系统保持快速、可靠、可扩展和易维护的模式。本文介绍了每个开发者都应该了解的十二个基础概念:负载均衡、缓存、内容分发网络、消息队列、发布—订阅系统、API 网关、断路器、服务发现、分片、速率限制、一致性哈希和自动扩缩容。
这些并不是什么抽象的面试题,而是现代软件背后看不见的运转机制。🚀
负载均衡:在服务器崩溃之前分散流量 ⚖️
负载均衡器位于用户与应用服务器之间。它不会把所有请求都发往同一台服务器,而是将传入流量分配到多台服务器上。
这一点很重要,因为不应该让任何一台服务器承担全部负载。如果某台机器不堪重负,用户就会遇到响应缓慢或请求失败。如果一台服务器宕机,负载均衡器可以把流量转发到健康的服务器。
负载均衡能够提升可用性、性能和可靠性。应用可以通过增加更多服务器来实现水平扩展,而不必依赖一台性能越来越强的机器。
在实际系统中,负载均衡器通常会采用轮询、最少连接、加权路由或基于 IP 的路由等算法。目标始终如一:把请求发送到能够高效处理它们的地方。
没有负载均衡,扩展就会变得脆弱;有了它,流量才会变得可控。🌍
缓存:记住计算成本高昂的答案 🧠
缓存是系统设计中最简单、也最强大的性能优化技术之一。
系统不必反复从数据库读取同一份数据,或重复计算相同的结果,而是可以将频繁访问的数据存储在高速内存中。当同样的请求再次到来时,系统可以直接从缓存提供结果,无须重复执行成本高昂的工作。
这既能缩短延迟,也能减轻后端系统的负载。
例如,如果成千上万名用户都在请求同一件商品的详细信息,应用就不应该每次都查询数据库。它可以把商品响应存入 Redis、Memcached、CDN 缓存或应用内存中,并在数据发生变化前重复使用。
缓存能让系统变快,但也会引入复杂性。缓存数据可能过期,缓存失效处理也可能很棘手。设计不当的缓存可能返回陈旧信息,或掩盖更深层的性能问题。
尽管如此,只要使用得当,缓存依然是架构中杠杆效应最高的工具之一。它能把重复工作变成即时响应。⚡
内容分发网络:让内容离用户更近 🌐
内容分发网络(Content Delivery Network,CDN)通过将静态资源存储在世界各地的边缘服务器上来提升性能。
当用户加载图片、视频、JavaScript 文件、样式表或静态文档时,请求不必每次都一路发往源站服务器。CDN 可以改由附近的边缘节点提供内容。
这能显著降低延迟。
如果附近的 CDN 节点已经缓存了副本,印度用户就不应该为了加载每一张图片都去访问美国的服务器。CDN 把内容放到离用户更近的位置,从而让应用显得更快、更可靠。
CDN 还能减轻源站服务器的负载、吸收流量峰值,并帮助抵御某些类型的滥用行为。
对于现代 Web 应用而言,CDN 并非可有可无的优化,而是实现全球化性能的基础架构之一。🚀
消息队列:解耦无须立即完成的工作 📬
并非所有任务都需要在用户请求期间完成。
用户上传文件、下单或触发报表时,有些工作可以异步执行。借助消息队列,一个组件可以把任务放入队列,另一个组件则在稍后进行处理。
这让生产者与消费者实现了解耦。
生产者不需要知道由谁处理消息,只需将工作加入队列,然后继续执行。消费者可以按照自己的节奏处理消息。
这种模式特别适合后台任务、邮件发送、图片处理、支付工作流、通知系统和长时间运行的任务。
消息队列可以吸收流量峰值,从而增强系统的韧性。如果流量突然上升,消息可以留在队列中等待处理,而不会压垮下游服务。
它还能提升可靠性。如果某个工作进程失败,通常可以重试对应的消息。
一个优秀的队列能把突如其来的压力转化为可控的处理过程。🔁
发布—订阅:一个事件,多种响应 📢
发布—订阅系统进一步扩展了消息传递的理念,允许多个消费者接收同一主题中的消息。
它不再是一个生产者向一个消费者发送一条消息,而是由生产者发布一个事件,再由多个订阅者各自独立响应。
假设有人在电商系统中创建了订单。订单服务发布一个 OrderCreated 事件;支付服务处理交易;库存服务更新库存;通知服务发送邮件;分析服务记录该事件。
每个服务都对同一个事件作出响应,却不与订单服务紧密耦合。
这种模式非常强大,因为它允许系统的各个部分独立演进。添加新的消费者无须修改生产者,生产者只需要发布事件。
发布—订阅架构常见于事件驱动系统、实时分析、微服务和流处理平台。
它让架构从直接调用转向事件流。✨
API 网关:后端的正门 🚪
API 网关充当客户端请求的统一入口。
客户端不再直接调用多个后端服务,而是调用网关。网关负责路由、身份认证、授权、速率限制、请求转换、日志记录,有时还会负责聚合响应。
这简化了客户端的使用体验。
移动应用不应该需要知道每个内部服务的端点,而应该调用一个稳定的 API 边界,再由网关决定把请求发送到哪里。
API 网关在微服务架构中尤其重要,因为它能够隐藏内部复杂性,还提供了一个集中实施安全规则与流量策略的位置。
不过,网关必须经过谨慎设计。如果所有组件都依赖它,它就会成为关键组件。网关性能不佳会影响整个系统。
优秀的 API 网关能让后端系统更易于使用,也能让系统以更安全的方式对外开放。🔐
断路器:停止调用已经发生故障的服务 🛑
断路器可以防止系统反复调用出现故障的依赖项。
在分布式系统中,一个服务通常依赖另一个服务。如果下游服务开始发生故障,上游服务可能会不断重试。这些重试会给本就举步维艰的系统施加更多负载,让故障进一步恶化。
断路器会监控故障率。当故障超过某个阈值时,它会“断开”电路,暂时停止调用该依赖项。
系统不再反复等待请求超时,而是快速失败或使用降级方案。
一段时间后,断路器可能进入半开状态,允许少量测试请求通过。如果这些请求成功,电路便会重新闭合;如果失败,电路则保持断开。
这种模式可以防止级联故障。
它提醒我们:韧性并不只是更加努力地重试,有时也意味着知道何时该停下来。⚠️
服务发现:动态寻找服务 🔎
在现代分布式系统中,服务实例会不断出现和消失。
容器会重启,Pod 会在节点之间迁移,自动扩缩容会添加新实例,部署过程会用新版本替换旧版本。
在这种环境中,硬编码服务地址并不可行。
服务发现通过自动跟踪可用的服务实例来解决这个问题。当一个服务需要调用另一个服务时,它会询问服务发现系统:健康的实例位于哪里?
这让系统可以保持动态变化。
客户端不再说“调用服务器 10.0.0.5”,而是说“帮我找一个健康的支付服务实例”。
服务发现常见于 Kubernetes、服务网格、云原生平台和微服务环境。
它让基础设施能够自由变化,而不会中断服务之间的通信。🧭
分片:拆分数据,让系统能够扩展 🗄️
随着数据不断增长,单个数据库可能变得过于庞大或缓慢,无法高效处理所有读写操作。
分片通过把数据拆分到多个数据库节点上来解决这个问题。
系统不再把所有用户都存储在一个数据库中,而是可以按照用户 ID 进行划分。一个分片存储 ID 位于某个范围内的用户,另一个分片存储另一个范围内的用户,以此类推。
这样,系统就能分散存储压力和工作负载。
分片很强大,但并不简单。选择正确的分片键至关重要。糟糕的分片键会造成负载不均,让一个分片不堪重负,而其他分片却未得到充分利用。
分片还会让查询变得更加复杂。如果一条查询需要访问多个分片,协调工作就会更加困难。
尽管如此,对于超大规模系统而言,分片通常必不可少。它让数据系统能够突破单个节点的容量极限,继续增长。📊
速率限制:保护系统免受滥用和过载 🚦
速率限制用于控制客户端在指定时间窗口内可以发出多少次请求。
这能够保护系统免受滥用、意外过载、数据抓取、暴力破解攻击和不公平资源占用的影响。
例如,一个 API 可以允许每位用户每分钟发出 100 次请求。如果用户超过这个限制,系统就返回 HTTP 429 Too Many Requests 之类的错误。
速率限制之所以重要,是因为后端资源是有限的。如果没有限制,一个制造大量噪声的客户端就可能拖慢所有人的使用体验。
它也是一种安全工具。登录端点、支付 API 和公共端点通常需要严格的限制,以防止滥用。
优秀的速率限制并不只是阻止用户,而是要实现公平、稳定和可预测的性能。🔐
一致性哈希:分配数据而无须大规模重组 🧩
一致性哈希是一种在多个节点之间分配数据的技术,可以在添加或移除节点时尽可能减少扰动。
它尤其适用于分布式缓存和数据库。
在简单的哈希系统中,添加一个新节点可能导致大多数键被重新映射。这意味着大量数据都需要迁移,成本高昂且会对系统造成严重干扰。
一致性哈希可以缓解这个问题。
它把节点和键都放在一个逻辑环上。当添加或移除节点时,只需移动一小部分键。
这使扩展过程更加平稳。
一致性哈希常见于分布式缓存、存储系统、数据库和负载分配系统。
它解决了一个细微却重要的问题:如何在不反复重新排列一切的情况下扩展分布式系统。🔄
自动扩缩容:自动增加和移除容量 📈
自动扩缩容让基础设施能够根据需求调整容量。
流量增加时,系统会添加更多计算资源;流量减少时,则会移除闲置资源。
这有助于在控制成本的同时维持系统性能。
例如,一个应用在正常流量下可能运行三台服务器。促销活动或产品发布期间流量激增,自动扩缩容会自动添加更多服务器;需求下降后,系统再缩减规模。
自动扩缩容通常以 CPU 使用率、内存使用率、请求数量、队列深度或自定义业务信号等指标为依据。
当应用采用无状态设计、健康检查足够可靠并且扩缩容策略经过仔细调优时,它的效果最好。
自动扩缩容无法消除架构问题,但能赋予系统弹性。它让基础设施根据真实世界中的流量变化作出响应,而不必依赖人工干预。☁️
这些概念如何协同工作 🧠
这十二个概念单独来看都很强大,但真实系统会把它们组合起来使用。
用户请求可能首先到达 CDN,由边缘节点提供静态内容。动态请求可能通过 API 网关,网关实施速率限制,再把流量路由到负载均衡器。负载均衡器把请求发送到通过服务发现找到的健康服务实例。
服务可能会先读取缓存。如果缓存未命中,它就读取分片数据库。如果请求会触发后台工作,服务就发布事件或把消息放入队列。如果某个依赖项开始发生故障,断路器会阻止级联故障。如果流量增加,自动扩缩容会添加更多实例。如果系统使用了分布式缓存,一致性哈希则有助于把数据分散到各个节点上。
现代系统就是这样存活下来的。
架构并不是某一种模式,而是对多种模式的谨慎组合。
开发者真正应该领悟的道理 🚀
理解这些概念,会改变你的思考方式。
你不再把后端系统仅仅视为简单的请求处理程序,而会开始把它们看作在压力下运行的生命系统。
你会在服务器过载之前考虑如何分配流量,在数据库不堪重负之前考虑缓存,在耗时任务阻塞用户之前考虑队列,在滥用发生之前考虑速率限制,在故障蔓延之前考虑断路器。
这就是架构思维的成熟。
你不需要立即掌握每一种工具,但应该理解每个概念所解决的问题。
一旦理解了问题,应该采用的模式自然就会变得清晰。
最后的思考 ✨
优秀的开发者所做的不只是编写能够运行的代码。
他们构建的是能够经受考验的系统。
负载均衡让流量保持可控;缓存让响应更加迅速;CDN 降低全球访问延迟;队列和发布—订阅系统实现工作解耦;API 网关简化访问;断路器防止级联故障;服务发现让动态系统保持连接;分片扩展数据容量;速率限制保护基础设施;一致性哈希减少变动带来的扰动;自动扩缩容适应需求变化。
这些概念共同构成了现代软件架构的基础。
如果你想从开发者成长为系统设计者,就应该深入学习这些模式。
因为在大规模场景下,软件并不只关乎逻辑。
它关乎韧性。⚙️💙
RECOMMEND