AiEDU

拒绝跟风:从单体到微服务,架构演进的终极指南

2025-12-01T00:00:00Z | 8分钟阅读 | 更新于 2025-12-01T00:00:00Z

FaceBook
拒绝跟风:从单体到微服务,架构演进的终极指南

这是老梁。

我在架构圈摸爬滚打十几年,带过几个人捣鼓的创业项目,也救火过日活千万级的庞大系统。见得多了,就发现一个残酷的真相:90%的系统崩盘,不是因为技术太弱,而是因为架构太贪。

很多兄弟在面试时把微服务吹得天花乱坠,一进公司落地就这就是灾难:服务拆得稀碎,一个请求跨十几个服务,延迟爆炸;分布式事务搞不定,账目无论如何对不上;排查一个Bug,日志在ELK里翻半天找不到头绪。

今天咱们不聊虚的,我把压箱底的经验拿出来,给你复盘一下:一个系统从单体到集群,再到微服务的真实演进路径。我会告诉你每个阶段的痛点在哪里,用什么算法技术去填坑。

这篇文章,建议你收藏,无论是做架构决策、解决生产故障,还是去面大厂的架构岗,都能用得上。


第一阶段:单体架构—— 别看不起它,它是赚钱机器

别一上来就整微服务。如果你的团队不到5个人,日活不到1万,强上微服务就是找死。单体架构是代码在内存中互调,没有网络开销,没有序列化损耗,它是性能的王者。

但单体怎么写,决定了你以后能不能拆得动。

1. 核心痛点与解决:代码腐化

很多单体项目后期变成了“大泥球(Big Ball of Mud)”,Controller调Service,Service之间随意乱调,甚至直接在Controller里写SQL。

架构师策略:模块化单体(Modular Monolith) 在这一阶段,虽然物理上是一个Jar包,但逻辑上必须严格隔离。

  • 战术落地:利用Maven多模块(Module)或严格的包结构(Package)隔离。
  • 设计原则:严格遵循 DDD(领域驱动设计)的界限上下文(Bounded Context)。
    • 比如 OrderService 绝对不能直接注入 UserMapper 去查库。
    • 必须通过 UserService 暴露的接口获取数据。
    • Why? 这样未来拆分时,只需要把 UserService 这个接口改成 Feign Client,业务逻辑一行都不用动。

2. 这里的技术栈

  • 核心:Spring Boot / GoFrame
  • 数据库:MySQL(单机)
  • 缓存:LocalCache (Guava/Caffeine) + Redis

第二阶段:集群架构(Cluster)—— 流量来了,怎么扛?

当你的单机 CPU 经常飙到 80%,或者单点故障导致半夜被叫醒重启服务时,你就该上集群了。 集群的本质是:水平扩展(Scale-out)。代码不用大改,部署多份,前面挡一层负载均衡。

但这个时候,原本在单机里不是问题的问题,全都冒出来了。

1. 难点一:会话丢失(Session Stickiness)

单机时,用户登录态存在 HttpSession(内存)里。上集群后,用户请求第一次打到机器A,第二次被Nginx分发到机器B,B没有内存数据,用户被迫下线。

技术选型与原理:

方案原理评价
Session 复制Tomcat之间广播Session垃圾。节点多了广播风暴会把内网带宽吃光。
Session 粘滞Nginx ip_hash 算法鸡肋。由于企业内网出口IP相同,会导致某台服务器流量倾斜;且机器宕机用户必掉线。
分布式 SessionSpring Session + Redis正解。状态下沉到Redis,服务变成无状态(Stateless)。

2. 难点二:数据库读写瓶颈

应用能扩展,数据库还是单点。写库如果锁表,读请求全卡死。

技术攻坚:读写分离(Read-Write Splitting)

  • 架构:一主(Master)多从(Slave)。写请求走Master,读请求走Slave。
  • 底层原理:MySQL Binlog 异步复制。
  • 致命坑:主从延迟(Replication Lag)
    • 场景:用户刚注册(写Master),立马自动登录(读Slave)。由于Binlog同步有毫秒级延迟,Slave还没数据,提示“用户不存在”。
    • 算法/策略
      1. 强制路由:对于即时性要求极高的读(如刚写入后的读取),代码层强制路由到主库。
      2. 缓存标记法:写入后在Redis打个标,读取时发现有标,就走主库,没标走从库。

3. 难点三:缓存一致性(Cache Consistency)

这是一个面试必问、工作必坑的点。数据库改了,缓存怎么删?

架构师铁律:Cache-Aside Pattern(旁路缓存模式)

  1. :先读缓存;命中返回;未命中读DB,回写缓存。
  2. 先更新DB,再删除缓存(注意是删除,不是更新!)。
    • Why删除? 更新缓存容易产生脏数据(并发写时顺序不可控)。
    • Why先更DB? 保证数据落盘是第一优先级。
    • 高级坑:删缓存失败了怎么办?
    • 解决方案Binlog 异步淘汰。订阅MySQL Binlog(使用 Canal),投递到 MQ,由消费者专门负责重试删除Redis,保证最终一致性

第三阶段:分布式微服务架构(Microservices)—— 治理复杂度的战争

什么时候上微服务? 不是因为QPS高,而是因为人多。 当50个后端开发在一个Git仓库里提交代码,Merge Conflict 就要花半天;当改一个“修改头像”的功能,却要整个系统停机发布;当“订单服务”需要扩容,却被迫连着“后台管理”一起扩容时。 微服务,是为了解决研发效率和独立部署的问题。

进入微服务,你面对的不再是代码逻辑,而是分布式系统的八大谬误

1. 核心技术选型:Spring Cloud Alibaba 战队

我们要构建的是一套生态,目前国内最稳的选型:

  • 注册中心/配置中心:Nacos(CAP理论中支持AP/CP切换,性能吊打Eureka)。
  • 流量网关:Spring Cloud Gateway(基于WebFlux,响应式编程)。
  • RPC调用:Dubbo(高性能) 或 OpenFeign(Http协议,生态好)。
  • 熔断限流:Sentinel。
  • 分布式事务:Seata 或 RocketMQ。

2. 难点一:服务发现与负载均衡算法

服务A要调服务B,B有100个实例,IP随时变。

  • 底层原理
    1. 注册:B启动时把IP:Port写入Nacos。
    2. 心跳:B每5秒发心跳,Nacos没收到就剔除。
    3. 拉取:A本地缓存一份B的实例列表(避免每次查Nacos)。
  • 负载均衡算法(Load Balancing)
    • Round Robin(轮询):简单,但没考虑机器性能差异。
    • Least Active(最小活跃数):Dubbo默认。谁处理得快,就多给谁发请求。这在生产环境最实用,能自动平滑慢节点的问题。
    • Consistent Hashing(一致性哈希):用于有状态服务,保证同一参数请求打到同一台机器。

3. 难点二:分布式事务(Distributed Transaction)

这是微服务的最大噩梦。单体的 @Transactional 在跨服务调用时失效。

实战流派对比与选择:

方案适用场景复杂度性能
XA / 2PC强一致性要求,银行核心极高(锁资源长)
Seata (AT模式)业务并发不高,不想改代码低(框架代理)中(依赖全局锁)
TCC高并发,对一致性要求极高高(要写Try/Confirm/Cancel三个方法)
MQ 最终一致性互联网主流选择极高

架构师推荐方案:本地消息表 + MQ(可靠消息最终一致性) 这是大厂处理高并发资金流转的标准姿势,一定要掌握。

  • 逻辑分解
    1. 上游(订单服务):开启本地事务,插入订单表,同时插入一张“消息发送表”(状态:待发送)。这一步是原子的
    2. 异步发送:一个独立线程轮询“消息发送表”,把消息投递到MQ。
    3. 下游(积分服务):消费MQ,增加积分。
    4. 幂等性(Idempotency):下游必须要防重!利用Redis或数据库唯一索引,保证同一条消息只处理一次。
    5. 兜底:如果下游处理失败,MQ会自动重试。如果死信,人工介入。

4. 难点三:雪崩效应与熔断(Circuit Breaker)

服务B挂了,服务A还在疯狂调B,导致A的线程池被耗尽,A也挂了。A的调用方C也就挂了。全线崩溃。

技术解法:Sentinel / Hystrix

  • 核心逻辑
    • 熔断:当错误率达到阈值(如50%),断路器打开(Open)。后续请求直接返回失败,不再调B。
    • 半开(Half-Open):过5秒,放一个请求过去试探。如果成功,关闭断路器;失败,继续打开。
  • 限流算法(Rate Limiting)
    • 令牌桶(Token Bucket):以恒定速率放令牌,请求拿令牌。允许突发流量
    • 漏桶(Leaky Bucket):以恒定速率处理请求。平滑流量
    • 实战建议:一般网关层用令牌桶应对突发流量,内部核心服务用漏桶保护数据库。

5. 难点四:全链路追踪(Observability)

微服务上线后,用户报障:“我点不下单”。请求经过了网关->订单->库存->支付。你根本不知道断在哪。

技术栈:SkyWalking / Jaeger

  • 原理:TraceID 透传。
    • 请求一进网关,生成一个全局唯一的 TraceID
    • 通过 HTTP Header 或 RPC Context,把这个 ID 一层层传下去。
    • SkyWalking Agent 字节码增强,自动采集调用耗时、SQL语句、异常栈。
    • 最终在 UI 上呈现一个完整的调用链路图(Flame Graph)

架构师总结:如何做决策?

最后,作为架构师,我想给你一个决策矩阵

  1. 初创期(0-1):毫不犹豫选单体(Modular Monolith)。活下去比架构完美更重要。别搞微服务,除非你想把公司搞黄。
  2. 增长期(1-10)集群 + 读写分离 + Redis缓存。这是性价比最高的优化手段,能抗住绝大多数中型互联网公司的流量。
  3. 爆发期(10-100):当你的团队规模超过20人,或者业务领域(订单、支付、用户)极其复杂且独立时,再考虑微服务

记住:架构是演进出来的,不是设计出来的。 能用简单的技术解决问题,才是真正的技术大牛。

© 2023 - 2025 - AiEDU | BLOG

知识改变命运,AI改变知识

关于我

Hi,这里是 AiEDU 的博客。AiEDU 是我在互联网上经常使用的名字。

我是一个热衷于开源的研发工程师,在这里我会记录一些关于技术等知识。欢迎你通过评论或者邮件与我交流。

知识共享(Creative Commons)

此网站的所有内容都遵循 CC BY-NC-SA 4.0

All contents of this website are licensed under CC BY-NC-SA 4.0.

社交链接