架构进化之路
小哲的「轻记账」已经长成 100 人团队、500 万行代码的庞然大物——但它还是那个创业第一天写下的「单体应用」。发布要 2 小时、改一行代码全量回归、一个内存泄漏拖垮全站。周师傅说:该进化了。这一本,从单体到微服务,把架构演进的全路线走一遍——包括那些「其实不该拆」的警告。
巨石阵的崩溃
当「一个应用」变成「一座山」师傅,出大事了!我们的「轻记账」现在 500 万行代码、100 个开发在同一个仓库里改——昨天发布等了 2 小时,凌晨上线后一个内存泄漏,全站瘫痪 40 分钟!老板说「必须拆微服务」,今天就要方案!
冷静。先说最重要的一句话:「必须拆微服务」这句话本身就值得怀疑。架构不是越新越好,是「匹配当前阶段」最好。我先带你走一遍完整的进化路线,让你自己判断——你们到底该不该拆、怎么拆。
记住一句话:微服务不是银弹,是「用复杂度换灵活性」的交易——拆之前先想清楚,你现在的痛,微服务真能治吗?走,第一站,从最开始讲起。
单体架构:一切的起点
Monolith · 三层架构 · 部署单元被吐槽最多的「单体」,其实是最伟大也最务实的架构——全世界 90% 的软件都是单体,而且大多数「就该是单体」。
单体架构(Monolith):整个应用打成一个包、部署成一份——前端页面、业务逻辑、数据库访问全在一个进程里。经典形态是三层架构:表现层(页面/API)→ 业务层(逻辑)→ 数据层(数据库)。
你创业第一天写轻记账时,就是最标准的单体——而且那是当时唯一正确的选择:一个人、一个包、一个数据库,部署简单、调试直接、没有网络开销。
┌──────────────────────────────────┐
│ 轻记账 单体应用 │
│ ┌──────────┐ ┌──────────────┐ │
│ │ 表现层 │ │ 业务层 │ │
│ │ 页面/API │→ │ 记账/用户/报表 │ │
│ └──────────┘ └──────┬───────┘ │
│ ┌────────────────────┴───────┐ │
│ │ 数据层:ORM + SQL │ │
│ └──────────────────┬─────────┘ │
└─────────────────────┼────────────┘
↓
MySQL 数据库(单库)
一个部署包 → 一台/一组服务器 → 整体上线、整体回滚
【单体的优点(别急着嫌弃)】
简单:一个仓库、一个部署、一个调试环境
快:函数直接调用,没有网络开销(最最关键!)
好测:一套测试环境全覆盖
事务好做:一个数据库,ACID 随便用
【单体的缺点(长大后的痛)】
代码耦合:改一行可能影响全局
发布沉重:500 万行全量构建、全量回归
扩展粗放:只能整体扩容(哪怕只有报表模块吃资源)
技术锁定:换语言/框架?等于重写
轻记账创业期:1 个后端、1 个前端、1 个数据库,单体应用两周上线——如果当时拆微服务,光服务间通信和部署环境就能拖死团队。单体不是「落后」,是「在那个阶段最正确」。
单体术语
- 单体架构(Monolith):一个应用、一个部署单元、一个数据库。
- 三层架构:表现层 / 业务层 / 数据层——单体的经典分层。
- 部署单元:一次发布的最小单位——单体就是「整个应用」。
- 耦合:模块间相互依赖的程度——单体的原罪。
- 整体扩容:加机器只能整包复制——资源利用率低。
本站收获:单体 = 简单、快、好测——创业期的唯一正解。它的缺点只在「长大之后」才发作:耦合、发布重、扩容粗、锁技术。别骂单体,先问自己配不配得上它。
模块化:单体的自我优化
分层 · 模块化单体 · 依赖倒置 · 模块边界拆微服务之前,先试试「拆模块」——一个部署单元里,把代码按业务切成边界清晰的模块。很多时候,这已经解决 80% 的痛点。
模块化单体(Modular Monolith):还是打成一个包部署,但内部按业务模块严格分层、边界清晰——记账模块、用户模块、报表模块互不直接调内部类,只通过「定义好的接口」通信。
关键设计原则:依赖倒置(高层模块不依赖低层模块的实现,依赖抽象接口)、模块边界(禁止跨模块乱调)、接口隔离(对外只暴露该暴露的)。
它的好处:代码边界清晰 → 未来拆微服务时,模块就是现成的服务边界。它是「微服务的预备课」。
❌ 传统单体(一锅粥):
记账Service 直接 new 用户Dao、改报表的全局变量……
→ 谁也说不清边界,改一行怕三天
✅ 模块化单体(有边界):
┌─ 记账模块 ─┐ ┌─ 用户模块 ─┐ ┌─ 报表模块 ─┐
│ 内部类私有 │ │ 内部类私有 │ │ 内部类私有 │
│ 只暴露接口 │ │ 只暴露接口 │ │ 只暴露接口 │
└─────┬──────┘ └─────┬──────┘ └─────┬──────┘
└──────── 模块间只走接口 ──────────┘
仍是一个部署包,但内部是「乐高积木」,不是「一锅粥」
【依赖倒置(DIP)】
模块 A 需要模块 B 的能力 → 面向「B 的接口」编程
→ 换实现不影响 A,测试还能 mock(衔接第一本的概念)
【模块化单体的价值】
代码边界 = 未来服务边界(拆分成本大降)
编译/测试可以按模块增量(构建快了)
新人接手看得懂(有地图了)
—— 很多团队「想拆微服务」,其实拆到模块化就够了!
小哲团队花了 3 个月把 500 万行代码整理成 6 大模块(记账/用户/报表/账本/通知/管理后台),边界清晰后:构建从 2 小时降到 40 分钟、回归测试范围缩小、新人 1 周上手。老板问「微服务呢?」——周师傅答:先让模块化跑半年,让痛点点位现形,再决定拆哪个。
模块化术语
- 模块化单体:一个部署单元 + 内部清晰模块边界——微服务的「预备课」。
- 依赖倒置(DIP):面向接口编程,不面向实现——模块解耦的基石。
- 接口隔离:只暴露该暴露的——「最少知识原则」。
- 模块边界 = 服务边界:为将来拆分铺路。
- 何时够用:团队 < 50 人、发布频率能接受 → 模块化单体常常就是终点。
本站收获:模块化单体 = 一个包 + 清晰边界 + 面向接口。它是性价比最高的「中间态」——很多团队的终点其实在这里,微服务是可选的下一个台阶,不是必经之路。
天花板:为什么必须拆
痛点清单 · 康威定律 · 爆炸半径不是所有单体都要拆,但所有单体最终都会撞到「天花板」。这一站,列出「必须拆」的硬指标——用它们对照自己公司,别凭感觉。
单体的天花板,通常由四根柱子撑到极限:
- ① 团队协作:100 人改同一个仓库 → 合并冲突、互相踩脚。这背后有个铁律——康威定律:系统的架构,会复制组织的沟通结构。团队大了,单体必然被组织「逼」着拆。
- ② 发布频率:发布一次 2 小时、全量回归、上线要审批——业务要求每天发 10 次,单体根本供不上。
- ③ 扩展瓶颈:只有报表模块吃资源,却要整包扩容 10 台——浪费 90% 的钱。
- ④ 爆炸半径:一个模块内存泄漏 → 全站瘫痪。单体里「一颗老鼠屎坏一锅粥」,没有隔离。
【必须拆的硬指标(中 3 条以上才认真考虑)】
□ 团队 > 50 人挤一个仓库,合并冲突天天有
□ 发布一次 > 30 分钟,且要全量回归
□ 业务要求独立发布频率(某模块一周发 5 次)
□ 只有某个模块吃资源,却要整包扩容
□ 一个模块故障拖垮全站(爆炸半径 = 全部)
□ 尝试换技术栈(如某模块想用 Go 重写)被锁死
□ 新人上手 > 1 个月还搞不清模块边界
【康威定律(Conway's Law)】
「设计系统的组织,最终产生的架构,等同于组织的沟通结构」
5 人团队 → 天然单体;5 个独立小组 → 自然长出 5 个服务
→ 拆微服务之前,先看团队怎么组织的!
【爆炸半径】
单体:1 个模块炸 → 全站陪葬
微服务:订单服务炸 → 支付服务还能活(有降级预案的话)
【反直觉提醒】
痛点没到 → 拆了更痛(复杂度暴涨)
痛点到了 → 不拆也痛(发布瘫痪)
关键:用体检表打分,不要看别人拆就跟风
体检打分:100 人一仓库 ✅、发布 2 小时 ✅、报表模块拖垮全站 ✅、模块边界不清 ✅——四条硬指标全中!周师傅点头:你们是真的「该拆了」。但拆法不是「一次性大爆炸」,而是渐进式(第 10 站讲)。
天花板术语
- 康威定律:组织沟通结构决定系统架构——拆服务先拆团队。
- 爆炸半径:故障影响的扩散范围——微服务的核心价值之一。
- 发布频率:业务需要独立发版的节奏——单体供不上就说明该拆。
- 整包扩容 vs 按需扩容:单体的资源浪费点。
- 技术栈锁定:单体换语言 = 重写——「用 Go 重写报表模块」的念头就是拆分信号。
本站收获:拆不拆看「体检表」:团队、发布、扩容、爆炸半径四条硬指标,中 3 条以上才认真考虑。别跟风——痛没到,拆了更痛;痛到了,不拆更痛。
微服务初识:按业务切分
微服务定义 · DDD · 限界上下文 · 拆分原则微服务不是「把代码拆小」,是「按业务能力切分自治单元」——每个服务有自己的数据、自己的生命周期、自己的团队。这一站讲拆分的「哲学」。
先给微服务下个准确定义——它不是「小单体」,而是:按业务能力拆分、独立部署、独立扩展、独立拥有数据的小型自治服务。
拆分的「指南针」是 DDD(领域驱动设计):先按业务领域画地图——限界上下文(Bounded Context) 就是服务的天然边界。记账是一个上下文、用户是一个上下文、支付是一个上下文——每个上下文内聚、上下文间松耦合。
原则很简单:按「业务能力」拆,不按「技术层」拆——不是「前端服务/后端服务/数据库服务」,而是「订单服务/库存服务/支付服务」。
❌ 错误拆法(按技术层拆):
表现服务 → 业务服务 → 数据服务(等于把单体竖切三刀,更乱!)
✅ 正确拆法(按业务能力拆):
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ 用户服务 │ │ 账本服务 │ │ 支付服务 │ │ 报表服务 │
│ 用户/登录 │ │ 记账/分类 │ │ 支付/退款 │ │ 统计/分析 │
└────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘
└──── 各自独立部署 · 各自独立数据库 ────┘
【DDD 核心概念】
限界上下文:业务领域内「内聚的边界」(账本和支付是两个世界)
领域模型:每个上下文内部的语言和规则
聚合根:边界内的一致性入口(如「订单」聚合)
→ DDD 地图画清楚了,服务边界就出来了
【好服务的五条标准】
独立部署:改它不影响别人(CI/CD 各自跑)
独立扩展:报表吃资源,只扩报表
独立故障:它挂了别人能降级活
独立数据:数据库归它独有(关键!)
独立团队:一个服务一个「小队」负责(康威定律)
微服务术语
- 微服务(Microservices):按业务能力拆分、独立部署/扩展/拥有的自治服务。
- DDD(领域驱动设计):按业务领域建模——微服务拆分的「地图学」。
- 限界上下文:领域的内聚边界——服务边界的天然依据。
- 聚合根:边界内一致性的入口(订单聚合)。
- 独立五要素:部署/扩展/故障/数据/团队——缺一个都不算真微服务。
- 反模式:分布式单体:拆了服务但数据共库、部署共发——假微服务,真灾难。
本站收获:微服务 = 按业务能力(DDD 限界上下文)切分的自治单元,独立部署/扩展/故障/数据/团队五要素缺一不可。别按技术层拆,别拆成分布式单体。
服务通信:怎么说话
REST/gRPC · 消息队列 · API 网关 · 服务发现 · 熔断单体里函数直接调用;微服务里,调用要跨网络——这是最大的变化,也是最多的坑。服务间怎么说话,直接决定系统稳不稳。
服务间通信两大流派:
- 同步:A 调 B,等结果——REST(HTTP+JSON,通用简单)或 gRPC(高性能二进制,内部服务首选)。
- 异步:A 发消息到队列,不等 B——消息队列(Kafka/RabbitMQ,第九本的老朋友),解耦 + 削峰。
光有通信协议还不够,四个「基础设施」必须配套:
- API 网关:所有请求的统一入口——鉴权、限流、路由(服务对外的门卫)。
- 服务发现:服务地址会变(扩容/重启)——注册中心(Nacos/Consul)让大家互相找得到。
- 负载均衡:一个服务多个实例——谁闲谁干(衔接第二本)。
- 容错三件套:超时、重试、熔断——B 挂了别把 A 拖死(熔断 = 快速失败 + 降级兜底)。
用户点击「下单」
↓
API 网关(鉴权 + 限流 + 路由)
↓
订单服务 ──同步调用──→ 用户服务(查用户)
↓
订单服务 ──同步调用──→ 库存服务(扣库存)⚠️ 可能失败
│ 失败怎么办?熔断器打开 → 快速失败 → 返回「库存不足」
↓
订单服务 ──异步发消息──→ Kafka
↓
通知服务(消费消息 → 发短信/推送)——不用等,异步解耦
【容错三件套(灵魂)】
超时:等 3 秒没响应就当失败(别无限等)
重试:短暂故障重试 2 次(但要小心雪崩!)
熔断:连续失败 N 次 → 打开熔断器,直接快速失败
→ 给下游喘息时间,防止「故障雪崩」(衔接第七本)
【同步 vs 异步怎么选】
需要结果马上用(查用户)→ 同步
不急着要结果(发通知)→ 异步
高并发削峰(订单洪峰)→ 异步队列
大促瞬间库存服务扛不住——如果同步硬等,订单服务跟着全挂,雪崩到全站。用了熔断 + 降级:库存服务超时 → 熔断器打开 → 订单服务直接返回「暂时拥挤,稍后再试」→ 用户服务、支付服务毫发无损。爆炸半径从「全站」缩小到「下单功能」。
通信术语
- 同步 vs 异步:REST/gRPC 等结果 vs 消息队列不等——按需选择。
- API 网关:统一入口:鉴权、限流、路由——服务的前台接待。
- 服务发现 / 注册中心:服务互相找到对方的「通讯录」。
- 熔断 / 降级 / 限流:容错三件套——微服务稳定性的命根子。
- 故障雪崩:一个服务慢 → 拖垮调用方 → 连锁反应——必须用熔断切断。
- gRPC vs REST:高性能二进制 vs 通用 HTTP——内部 vs 对外。
本站收获:服务通信 = 同步/异步两条路 + 网关/注册中心/负载均衡 + 超时重试熔断。微服务的第一课:调用是会失败的——把「失败」设计进系统,而不是祈祷它不发生。
数据拆分:最难的一关
独立数据库 · Saga · 最终一致 · CQRS · 事件溯源微服务里最痛的坑不是代码,是数据——单体里一个事务搞定的事,拆开后变成「跨服务的分布式难题」。这一站讲清楚数据怎么拆、一致性怎么保。
铁律第一条:每个服务独享自己的数据库——服务之间绝不能「共库」(共库 = 假微服务 = 分布式单体,改表结构牵一发动全身)。
但数据一拆,老问题来了:单体里「下单 + 扣库存 + 减余额」是一个本地事务,原子完成;拆开后跨三个库,怎么办?三个答案:
- 两阶段提交(2PC):强一致但慢、脆弱——生产基本不用。
- Saga 模式:把一个长事务拆成一系列本地事务 + 补偿——下单→扣库存→减余额,任何一步失败,执行「补偿」回滚前面(退款、加库存)。主流方案。
- 最终一致:不追求「瞬间一致」,接受「过一会儿一致」——订单创建了但积分 5 秒后到账,用户无感。
【正常流程】
订单服务:创建订单 ✓ → 扣库存 ✓ → 扣款 ✓ → 完成!
每一步都是独立本地事务(自己的库)
【失败流程(第 2 步扣库存失败)】
订单服务:创建订单 ✓ → 扣库存 ✗
→ 启动补偿:
补偿「取消订单」(回滚第 1 步)
→ 最终结果:数据回到原点,用户看到「下单失败」
【Saga 两种编排】
编排式:中央 Saga 协调器指挥每一步(可控)
协同式:服务间通过事件互相触发(解耦,但难追踪)
【最终一致的实践】
消息队列 + 幂等消费:积分服务消费「订单创建」事件
→ 5 秒内到账;重复消费?幂等键去重(第九本老朋友)
【CQRS(读写分离)】
写走订单库,读走报表库(事件同步过去)
→ 读写各自优化——报表服务不再拖累写链路
【事件溯源(Event Sourcing)】
不存「当前状态」,只存「事件流」——状态由事件重放
→ 能回到任意历史状态(和第九本 Iceberg 时间旅行异曲同工)
数据拆分术语
- 服务独享数据库:微服务铁律——共库 = 假微服务。
- Saga 模式:本地事务链 + 补偿回滚——分布式事务的主流解。
- 最终一致:不追求瞬间一致,接受短时不一致——大多数业务的现实答案。
- 幂等性:同一消息消费 N 次结果一致——消息场景的保命符。
- CQRS:读写分离——查询和命令各走各的路。
- 事件溯源:只存事件流——状态可重放、可回溯。
- 2PC:两阶段提交——教科书有,生产很少用。
本站收获:数据拆分 = 服务独享库 + Saga 补偿 + 最终一致 + 幂等消费。拆服务容易,拆数据难——跨库事务的答案不是「更强的事务」,而是「接受最终一致 + 设计好补偿」。
部署运维:容器与 K8s
Docker · Kubernetes · 滚动发布 · 弹性伸缩单体一台服务器就够;微服务几十个服务、上百个实例——没有容器化和编排,运维会被活活累死。这一站,把部署问题讲透。
微服务部署三板斧(都是前几本的老朋友,这里串成体系):
- 容器化(Docker):每个服务打包成镜像——「环境固化」,在哪儿跑都一样(第六本讲过容器,这里用到极致)。
- 编排(Kubernetes/K8s):几百个容器怎么调度、怎么伸缩、怎么自愈——Pod 是运行单元、Deployment 管副本、Service 管访问、HPA 自动伸缩。
- 发布策略:滚动发布(一批批替换)、灰度发布(先 1% 用户试)——出问题随时回滚(第二本、第六本的 DevOps 技能在这里开花)。
┌──────────────────── K8s 集群 ────────────────────┐
│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │订单 │ │订单 │ │订单 │ ... │支付 │ │支付 │ ... │
│ │Pod │ │Pod │ │Pod │ │Pod │ │Pod │ │
│ └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ │
│ └──Service(订单)──┘ └─Service(支付)─┘ │
│ · Deployment:声明「要 3 个副本」,坏了自动补 │
│ · HPA:CPU 超 70% → 自动扩到 10 个 │
│ · Ingress:外部流量入口 → 路由到 Service │
└──────────────────────────────────────────────────┘
【部署流程(DevOps 完整链)】
git push → CI 构建镜像 → 推镜像仓库
→ CD 更新 Deployment → 滚动发布(一批批换,不中断)
→ 健康检查失败?自动回滚上一版
(第二本《软件王国建造记》第 6 站的完整落地!)
【发布策略对比】
滚动发布:一批批替换,始终有可用实例
蓝绿部署:两套环境切流量(第六本讲过)
灰度/金丝雀:1% 用户试新版,稳了再全量
→ 微服务时代:每个服务各自灰度,互不牵连
【K8s 学习要点】
Pod(最小单元)/ Deployment(声明式管理)/ Service(访问入口)
ConfigMap/Secret(配置与密钥)/ Ingress(网关)
微服务化后:大促时只有报表服务吃资源——K8s HPA 自动把报表服务从 2 个实例扩到 15 个,其他服务纹丝不动;促销结束自动缩回 2 个。这就是「独立扩展」的威力——单体时代得整包扩 10 台,现在只扩需要的那一个。
部署术语
- Docker 镜像:服务 + 环境打包——「哪儿跑都一样」。
- Kubernetes(K8s):容器编排平台——Pod/Deployment/Service/HPA。
- 滚动 / 蓝绿 / 灰度发布:三种发布策略——中断越少越好。
- 弹性伸缩(HPA):按负载自动扩缩容——按需付费的基石。
- 自愈:Pod 挂了自动重建——K8s 的「自我修复」。
- ConfigMap/Secret:配置与密钥管理——微服务的「环境变量抽屉」。
本站收获:部署 = Docker 固化环境 + K8s 编排调度 + 滚动/灰度发布 + HPA 弹性伸缩。微服务的运维复杂度,全靠容器化 + 编排「压下去」——没有 K8s,几十个服务没法活。
可观测性:微服务的眼睛
日志 · 指标 · 链路追踪 · OpenTelemetry · SLO单体排查问题:看一个应用的日志就行。微服务排查:一个请求跨 10 个服务——没有「眼睛」,故障来了只能靠猜。可观测性三支柱,微服务的标配。
可观测性三大支柱:
- 日志(Logs):所有服务日志集中收集(ELK/Loki)——出错能翻「案发现场」。
- 指标(Metrics):每个服务的 QPS、延迟、错误率(Prometheus + Grafana)——状态一眼可见(衔接第七本监控)。
- 链路追踪(Tracing):一个请求从网关到订单到库存的完整旅程——TraceID 串起所有服务的日志,SkyWalking/OpenTelemetry 是标配。故障慢在哪一跳,一目了然。
再加两个管理概念:SLO(服务等级目标)——「下单接口 P99 延迟 < 500ms」,达到与否是团队的 KPI;告警——SLO 被突破自动报警(第七本的老朋友)。
用户请求(TraceID: abc-123)
↓ 网关 span: 鉴权 2ms
↓ 订单服务 span: 创建订单 20ms
↓ 用户服务 span: 查用户 15ms ← 这里慢了?!
↓ 库存服务 span: 扣库存 300ms ← 找到瓶颈!
↓ 通知服务 span: 发消息 5ms(异步)
TraceID 贯穿所有日志 → 按 ID 一搜,全程时间轴都在
→ 「请求慢」从玄学变成科学:P99 延迟、每跳耗时、瓶颈定位
【三支柱分工】
日志:具体报错是什么(what happened)
指标:系统整体状态如何(how is it going)
追踪:单个请求经历了什么(where is the problem)
【技术选型】
日志:ELK / Loki + Grafana
指标:Prometheus + Grafana
追踪:SkyWalking / OpenTelemetry + Jaeger/Tempo
一体化:阿里云 ARMS / 腾讯云 APM(托管省事)
【SLO 与告警】
SLO:目标(P99 延迟 < 500ms)
SLI:实际测量值(当前 P99 = 800ms)
告警:SLI 突破 SLO → 告警 → 值班(第七本联动)
用户反馈「下单很慢」。单体时代:翻日志找半天;微服务时代:SkyWalking 打开链路追踪 → 发现 90% 时间卡在「库存服务」→ 点进去看到数据库慢查询 → 加索引 → 解决。全程 5 分钟——可观测性就是微服务时代的「破案工具」。
可观测性术语
- 三支柱:日志(what)、指标(how)、追踪(where)——缺一不可。
- TraceID / Span:一次请求的全局 ID / 每一跳的时间片段。
- 分布式链路追踪:SkyWalking / OpenTelemetry——跨服务排障神器。
- SLO / SLI:服务目标 / 实际表现——把「稳不稳」变成数字。
- 集中日志 / 指标平台:ELK + Prometheus + Grafana——微服务标配三件套。
- 告警与值班:SLO 被突破自动告警——可观测性的「最后出口」。
本站收获:可观测性 = 日志(what)+ 指标(how)+ 追踪(where)+ SLO(目标)。微服务没有眼睛就是瞎子——这四样配齐,故障从「猜」变成「查」。
服务网格:云原生进阶
Service Mesh · Sidecar · 云原生 12 要素 · Serverless微服务越拆越多,熔断、限流、重试这些「公共能力」在每个服务里重复实现——太烦了。服务网格把这些能力从代码里「抽出来」,下沉到基础设施层。
服务网格(Service Mesh)的核心思想:把「服务间通信的通用能力」(熔断、限流、重试、鉴权、链路追踪)从业务代码里剥离出来,用轻量代理(Sidecar)在每个服务旁边实现——业务代码只关心业务,通信能力统一由 数据面(Sidecar 们)执行、控制面(如 Istio)下发策略。
它带来的进化:流量治理从「代码」变成了「配置」——想给支付服务加限流?控制台改一行 YAML 下发,不用改任何代码、不用发版。
再往上,就是 云原生:12 要素(配置外置、无状态、日志流式……)+ K8s + 容器——以及 Serverless(FaaS):连服务器都不用管了,按调用付费。
┌──────────── 控制面(Istio)────────────┐
│ 策略下发:熔断/限流/重试/鉴权/流量比例 │
└──────┬───────────────────────────┬──────┘
│ 下发配置 │
┌────┴────┐ ┌────┴────┐ ┌────┴────┐
│订单服务 │ │支付服务 │ │库存服务 │
│ +Sidecar │ │ +Sidecar │ │ +Sidecar │ ← 数据面
└────┬────┘ └────┬────┘ └────┬────┘
所有流量都走 Sidecar(透明代理)
【优点】
流量治理不写代码:改配置即生效(灰度 10% 流量,一行 YAML)
可观测增强:Sidecar 自动上报全链路追踪
多语言统一:Java/Go/Python 服务用同一套治理能力
【代价(诚实说)】
性能损耗:每个请求多一跳代理(几毫秒)
复杂度转移:把「代码复杂度」换成了「运维复杂度」
→ 服务少(<30 个)时,服务网格是过度设计!
【云原生 12 要素(速览)】
配置外置 / 无状态 / 日志流式 / 开发生产一致
/ 依赖显式声明 / 进程无共享……(网上有完整清单)
【Serverless / FaaS】
连容器都不用管:写个函数,平台自动跑
极致弹性:按调用次数付费,冷启动有延迟
适合:任务型/事件驱动型(消息处理、图片处理)
进阶术语
- 服务网格(Service Mesh):通信能力下沉到 Sidecar——熔断限流不写代码。
- Sidecar 代理:服务旁边的「交通协管」——数据面。
- 控制面 / 数据面:策略大脑 / 执行代理——Istio 是代表。
- 云原生 12 要素:云上应用的最佳实践清单。
- Serverless(FaaS):连服务器都不用管——按调用付费。
- 适度原则:服务少时别上服务网格——技术选型要匹配规模。
本站收获:服务网格 = 把熔断限流从代码里抽到 Sidecar——改配置即治理。云原生 = 12 要素 + K8s + Serverless。但记住:规模不到,别硬上——高级架构是给「需要的人」准备的。
演进路线:拆分的艺术
绞杀者模式 · 渐进式拆分 · 避坑 · 什么时候不该拆最后一站,把整条进化路线串成「实操地图」——怎么从 500 万行单体安全地走到微服务,以及最重要的:哪些坑是前人用命踩出来的。
核心心法八个字:渐进式拆分,绝不重写。「把单体推倒重写微服务」是行业最大灾难——重写两年,业务停了,团队崩了,最后发现旧系统还能用。
正确姿势是 绞杀者模式(Strangler Pattern):像藤蔓慢慢绞杀老树——每次只切一个模块出来变成服务,老的照常跑,新的慢慢接管。切一块、稳一块、再切下一块。
【阶段一:模块化(3 个月)】
单体内部先划清模块边界(第 2 站)→ 为拆分铺路
【阶段二:第一个服务(优先选「低耦合高价值」)】
先拆哪个?打分:
· 业务独立性强(报表模块 —— 只读,不参与写链路)
· 吃资源大户(单独扩容收益最大)
· 团队边界清晰(谁负责谁拆)
→ 报表模块 → 报表服务(独立部署+独立库+独立团队)
【阶段三:绞杀循环(持续)】
每 1-2 个月绞杀一个模块:
切接口 → 迁移数据 → 切流量 → 删老代码
→ 老单体越来越瘦,新服务越来越多
→ 每一步都可回滚,业务零中断
【血泪避坑清单(前人踩出来的)】
✗ 一次性大爆炸重构(灾难)
✗ 拆成「分布式单体」(共库共发布 = 假微服务)
✗ 每个服务各自为政(统一规范:网关/日志/追踪要统一)
✗ 忘记数据迁移(服务拆了,库没拆 = 白拆)
✗ 团队没跟上(拆服务先拆组织,康威定律!)
【什么时候不该拆(诚实建议)】
团队 < 30 人 / 发布不频繁 / 单体还没乱 → 别拆!
先把单体整理好(模块化)就是进步——不是所有公司都需要微服务
第 1-3 月:单体模块化(6 大模块边界清晰);第 4 月:拆出报表服务(吃资源大户,独立扩容);第 6 月:拆出用户服务(独立团队接手);第 9 月:拆出账本服务(数据迁移用了双写过渡);第 12 月:支付链路重构为 Saga。老单体从 500 万行瘦到 120 万行——发布从 2 小时到 15 分钟,爆炸半径从「全站」到「单服务」。
演进术语
- 绞杀者模式(Strangler):渐进式拆分——藤蔓绞杀老树,一步一块。
- 渐进式 vs 大爆炸:持续演进 vs 推倒重写——前者是工程,后者是赌博。
- 双写过渡:数据迁移期间新旧库同时写——平滑切换的标配。
- 统一规范:网关/日志/追踪/部署标准统一——防「万国牌」。
- 拆分优先级:低耦合 + 高价值 + 团队清晰——先拆软柿子。
- 反模式:分布式单体:假微服务——比真单体还难维护。
- 规模匹配:人少就别拆——模块化可能就是终点。
本站收获:演进 = 先模块化 → 绞杀者模式渐进拆分 → 统一规范 + 数据迁移跟上。拆分是艺术不是技术:宁可慢,不要断;规模不到,别硬拆——「渐进式拆分,绝不重写」。
架构对比总表 + 路线图
最后一页:选型决策表三大架构对比
| 维度 | 单体 | 模块化单体 | 微服务 |
|---|---|---|---|
| 部署单元 | 1 个 | 1 个(内部模块化) | N 个独立 |
| 团队规模适配 | < 20 人 | < 50 人 | 50 人+(按服务分队) |
| 发布频率 | 低(周/月) | 中(天) | 高(每天多次) |
| 扩展粒度 | 整包扩容 | 整包扩容 | 按服务独立扩 |
| 故障影响 | 全站 | 全站(模块内可控) | 单服务(可降级) |
| 事务处理 | 本地事务(简单) | 本地事务(简单) | Saga/最终一致(复杂) |
| 排障难度 | 低 | 中 | 高(需可观测性) |
| 运维成本 | 低 | 中 | 高(K8s/网格) |
| 适合阶段 | 创业期 | 成长期 | 规模化期 |
一句话决策口诀
- 团队小、发布少 → 单体(别折腾)
- 团队中、有点乱 → 模块化单体(性价比最高)
- 团队大、发布频、爆炸半径受不了 → 微服务(按绞杀者模式走)
- 微服务已经很顺 → 考虑 服务网格/云原生(规模到了才上)
是「用复杂度换灵活性」的交易。
单体无罪,模块化够用就别拆;
真要拆,就学绞杀者:渐进式拆分,绝不重写;
而所有架构进化的终点,都是同一个词——适配。