三、系统架构演进:从单体到微服务
聊到高并发优化,架构层面的演进是绕不开的坎儿。很多人一上来就追微服务、容器化,说实话,连单体都没玩明白呢。
我见过太多团队,业务量刚上来就盲目拆分,结果拆分完反而更慢了。为什么?因为没搞懂架构演进的核心驱动力是什么。
说白了,架构不是设计出来的,是逼出来的。
3.1 单体架构:起点,但别迷信
早几年,所有代码都扔在一个工程里。业务逻辑、数据访问、甚至前端模板,全混在一起。这挺爽的——开发快,部署简单,一个包搞定。
我曾经在一个日活千万的项目里,硬生生扛着单体架构跑了一年半。为什么没拆?因为业务模型足够稳定,数据库扛得住,接口响应没崩。嗯,这里要注意,单体不是原罪,hold不住才是。
单体架构的核心特征
- 所有功能模块耦合在同一个进程内
- 部署单元单一,一个WAR/JAR包跑全部
- 数据源统一,事务由本地事务管理器控制
- 接口调用为进程内方法调用,延迟几乎为零
但高并发一来,问题就暴露了。
流量涨上去时,你想扩某个热点模块,不好意思,只能整个应用一起扩。一个模块OOM,整站瘫痪。我记得有一次双十一大促,就因为一个日志上报模块堵死了,结果核心交易接口也跟着断了。那种感觉,真的,一言难尽。
3.2 垂直拆分:先治标再治本
面对单体越来越臃肿,第一步往往不是上微服务,而是做垂直拆分。
我在一家电商公司时,把订单、商品、用户这三个模块拆成三个独立应用。每个应用独立部署,独立数据库。效果立竿见影——核心链路的稳定性提升了一大截。
| 对比项 | 单体架构 | 垂直拆分后 |
|---|---|---|
| 部署粒度 | 整个应用 | 按业务模块 |
| 扩容策略 | 整体扩缩容 | 按需扩缩容 |
| 故障隔离 | 一个模块故障全站挂 | 故障隔离到单个模块 |
| 数据耦合 | 共享数据库 | 独立数据库/Schema |
| 研发效率 | 代码冲突频繁 | 团队独立迭代 |
我的经验:垂直拆分别贪多,先拆最核心的流量集中区域。比如电商先拆订单和库存,社交先拆消息和关系链。拆完要留足数据同步的缓冲,我曾经吃过亏,拆完才发现两个模块之间的数据一致性校验没做全。
3.3 SOA 与微服务:本质区别在哪?
很多人分不清 SOA 和微服务。我个人的理解很简单:SOA 强调的是服务重用和ESB集成,而微服务强调的是去中心化和独立自治。
你想想看,SOA 时代,一个 ESB 总线往往成了新的性能瓶颈。微服务干脆去掉ESB,改成轻量级通信协议——HTTP/REST 或者 gRPC。
下面这张图,能帮你快速理清架构演进的脉络:
从单体走向微服务,本质上是一次控制权转移——把集中式的管控,分散到各个小团队自己手里。
3.4 微服务落地的几个坑
微服务不是银弹。我见过很多团队,服务拆了二三十个,但接口调用直接退化成分布式大泥球。为什么会这样?
我曾经踩过的坑:
- 拆分粒度太细,一个业务查询要串十几个服务,延迟从10ms飙升到800ms
- 缺少服务治理框架,熔断降级全靠手写if-else,上线即事故
- 分布式事务滥用,把Seata当万能药,结果全局锁导致性能倒挂
- 链路追踪没做全,线上问题定位全靠猜
我个人的经验是:拆分前先做可观测性。日志、指标、链路追踪这三个基建没做好,我不建议你碰微服务。
你想想看,找不到问题在哪,你怎么优化?
3.5 接口设计在架构演进中的角色
不管架构怎么变,接口始终是服务之间的契约。单体里是方法签名,微服务里变成了REST/gRPC定义。
这里面有个容易被忽略的点:接口兼容性。
我曾经在微服务升级时,因为一个接口字段的类型从String改成了Integer,结果下游消费方全崩了。嗯,从那以后,我的接口设计强制遵循三点:
- 字段只增不减——要废弃就用@Deprecated标记,别直接删
- 兼容性测试进CI——每次改接口,自动跑一遍契约测试
- 版本号不轻易升——尽量做到向后兼容,减少上下游同步成本
避坑指南:接口设计时,把枚举值、字典项用字符串传,别用int。你想想,哪天产品说加个状态,你int枚举怎么扩展?
3.6 小结:从架构视角看接口优化
架构演进对接口优化的影响,我总结成一句话:单体拼性能,微服务拼治理。
单体环境下,接口优化主要是代码级——SQL优化、缓存、异步。微服务环境下,你得先保证服务能稳定跑起来,再去谈优化。否则,一个服务挂了,你接口性能再好也没用。
记住,架构是手段,不是目的。别为了微服务而微服务,也别死守着单体不放。能解决问题,就是好架构。