1. 单体架构(Monolith)
定义:将应用的所有功能(前端、后端、数据处理、定时任务等)打包成一个单一的可部署单元(如一个 WAR 包、一个可执行 JAR、一个 Flask/Django 项目)。
特点:
- 单一代码库:所有模块代码放在一起。
- 单一部署单元:整个应用作为一个进程运行。
- 共享数据库:所有模块使用同一个数据库。
优点:
- 开发初期简单,易于调试和测试。
- 部署单一,运维成本低(只需管理一个服务)。
- 跨模块调用是进程内调用,性能高、无网络延迟。
缺点:
- 代码耦合:随着功能增加,代码变得复杂、难以维护。一处改动可能影响全局。
- 扩展瓶颈:只能整体水平复制(克隆整个应用),无法针对性能热点单独扩展。
- 技术锁定:很难在某个模块使用不同的技术栈。
- 部署影响:任何微小修改都需要重新部署整个应用。
典型场景:创业初期、小型内部系统、简单 CRUD 应用。
2. 前后端分离
定义:将前端(用户界面)和后端(业务逻辑、数据存取)分成两个独立的项目,通过 API 接口通信。
架构图:
[浏览器/移动端] --(HTTP/HTTPS)--> [后端 API 服务(REST/GraphQL)] --(ORM)--> [数据库]
^
| 返回 JSON/XML
核心特点:
- 前端:独立项目,通常使用 React、Vue、Angular 等框架,负责 UI 渲染和用户交互。
- 后端:只提供 API 接口(JSON/XML),不关心页面展示。
- 通信:无状态(通常使用 JWT 或 Session + 跨域 CORS)。
优点:
- 独立开发部署:前后端团队可以并行工作,独立版本迭代。
- 技术栈解耦:前端可以随意选择框架(React/Vue),后端可以选择最适合的语言(Python/Go/Java)。
- 多端复用:同一套后端 API 可同时服务 Web、移动 App、小程序、第三方客户端。
- 性能优化:前后端可以分别针对瓶颈优化(如前端做静态 CDN,后端做缓存)。
缺点:
- 增加沟通成本(API 契约定义、版本管理)。
- 首屏加载可能变慢(需额外请求数据),但可通过 SSR(服务端渲染)缓解。
- 对 SEO 不友好(纯 SPA 需预渲染)。
与单体架构的关系:前后端分离并不等于非单体。即使前后端代码分离部署,后端本身仍然可以是单体架构。
3. 微服务
定义:将单一应用程序划分为一组小型的、独立的服务。每个服务围绕业务能力构建,可独立开发、部署、扩展,使用轻量级通信机制(通常是 HTTP REST 或 gRPC)。
核心原则:
- 按业务边界拆分:例如订单服务、用户服务、商品服务、支付服务。
- 每个服务拥有自己的数据库:避免数据库层面的强耦合。
- 去中心化治理:每个服务可选用不同的技术栈。
- 基础设施自动化:需要服务发现、负载均衡、容错(熔断、重试)、分布式追踪等。
优点:
- 独立部署与扩展:修改某个服务只需重新部署该服务,且可针对高负载服务单独扩容。
- 故障隔离:某个服务崩溃不会导致整个系统瘫痪(但需要设计降级)。
- 技术多样性:新服务可以使用更适合的语言或框架。
- 团队自主:小团队可以拥有完整的服务生命周期。
缺点:
- 分布式系统复杂度:网络延迟、数据一致性(分布式事务)、服务发现、链路追踪等挑战。
- 运维成本高:需要容器编排(Kubernetes)、监控、日志聚合、API 网关等基础设施。
- 数据一致性难:跨服务的事务通常采用最终一致性(如 Saga 模式),而不是传统 ACID。
- 接口版本管理:服务间依赖升级困难。
何时适用:大型复杂系统、多个独立团队协作、需要频繁独立发布、弹性扩展需求高的场景。对于中小型项目,单体或模块化单体可能更合适。
与前后端分离的关系:前后端分离是用户界面与业务逻辑的解耦;微服务是后端内部的进一步拆分。两者可共存:前端调用 API 网关,网关路由到不同的微服务。
4. 消息队列
定义:一种异步通信组件,用于在分布式系统中解耦生产者和消费者,实现可靠、可扩展、高吞吐的消息传递。
核心模型:
- 生产者(Producer):发送消息到队列。
- 队列(Queue):存储消息的缓冲区(如 RabbitMQ、Kafka、Redis Streams)。
- 消费者(Consumer):从队列拉取或由队列推送消息进行处理。
常见模式:
- 点对点(Queue):一条消息只被一个消费者处理(任务队列)。
- 发布/订阅(Topic/Exchange):一条消息被多个消费者订阅(广播)。
典型场景:
- 异步处理:用户注册后发送欢迎邮件 → 不阻塞响应,放入队列让后台消费。
- 削峰填谷:秒杀系统将请求先写入队列,后端逐步处理,防止数据库被打死。
- 系统解耦:订单完成后,支付服务、库存服务、物流服务通过订阅“订单完成”消息来分别处理,互不依赖。
- 日志收集:应用将日志发往 Kafka,多个消费者分别做存储、分析、告警。
优点:
- 提高系统可伸缩性和弹性(消费者可以动态增减)。
- 缓冲突发流量,防止系统过载。
- 提供可靠重试和死信队列,减少失败丢失。
缺点:
- 增加系统复杂性和运维负担(需要管理消息中间件)。
- 消息顺序性、重复消费、消息积压等问题需要额外设计。
5. 缓存
定义:将数据副本存储在访问速度更快的存储介质中(如内存),以加速后续访问,减少对后端慢速存储(如数据库)的压力。
常见层级:
- 浏览器缓存(HTTP Cache-Control、Expires)
- CDN 缓存(静态资源)
- 反向代理缓存(如 Nginx、Varnish)
- 进程内缓存(如 Python functools.lru_cache、Guava Cache)
- 分布式缓存(如 Redis、Memcached)
缓存模式:
- 旁路缓存(Cache Aside):应用先读缓存,如果未命中则读数据库并回填缓存。
- 只读缓存(Read-Through):缓存组件自身负责回源加载。
- 写穿透(Write-Through):写操作同时更新缓存和数据库。
- 写回(Write-Behind):先写缓存,异步批量写数据库(高风险,可能丢数据)。
常见问题与解决:
- 缓存穿透:查询不存在的数据,大量请求直达数据库 → 使用布隆过滤器或缓存空结果。
- 缓存雪崩:大量缓存同时失效,请求涌向数据库 → 设置随机过期时间或使用热点数据永不过期+后台异步更新。
- 缓存击穿:某个热点数据失效,高并发请求同时穿透 → 使用互斥锁(如 Redis SETNX)或“永远不过期”。
为什么重要:
- 数据库 IO 通常是系统的瓶颈,缓存能将读性能提升几个数量级(毫秒 → 微秒级)。
- 尤其适用于读多写少、计算开销大的场景。
与消息队列的区别:缓存用于加速读/写,消息队列用于异步通信和解耦。
架构演进路径图
单体架构(前后端混合)
│
▼
前后端分离(后端仍是单体)
│
├── 继续单体(模块化单体)── 适用于中小型团队
│
└── 拆分为微服务 ── 适用于大规模、多团队
│
├── 需要消息队列进行异步解耦
├── 需要分布式缓存提升性能
└── 需要 API 网关、服务发现等设施
选择建议:
| 场景 | 推荐架构 |
|---|---|
| 个人项目、原型验证 | 单体(前端不分离或简单模板) |
| 小型产品(3-5 人团队) | 前后端分离 + 单体后端(模块化) |
| 中等规模(10 人以上,多端) | 前后端分离 + 单体后端(或按领域模块划分) |
| 大型系统(多团队,高并发) | 微服务 + 消息队列 + 缓存 + 服务网格 |
| 极高并发、读多写少 | 缓存优先(甚至全部缓存) + 异步落盘 |
不需要过度设计:如果团队没有运维分布式系统的能力,微服务会带来灾难。很多成功产品长期保持模块化单体,只在必要时拆分。
小结
| 概念 | 核心定位 | 典型技术 |
|---|---|---|
| 单体架构 | 所有功能打包成一个应用 | Django、Spring Boot、Rails |
| 前后端分离 | UI 与 API 解耦 | React/Vue + DRF/Flask/Spring MVC |
| 微服务 | 按业务垂直拆分后端 | Spring Cloud、Kubernetes、gRPC |
| 消息队列 | 异步解耦、削峰 | RabbitMQ、Kafka、Redis Streams |
| 缓存 | 加速读、减压后端 | Redis、Memcached、CDN |
评论
请登录后发表评论
暂无评论,快来发表第一条评论吧!