← 返回因果链

🔗 微服务因果链

因为…所以… — 从单体架构到微服务全链路的因果推导

服务拆分服务注册发现API网关 熔断降级链路追踪配置中心

📊 因果链总览图

❓ 单体应用
部署慢/扩展难
💡 服务拆分 ⚡ 地址硬编码
无法动态伸缩
💡 服务注册发现
⚡ 没有统一入口
认证/限流重复
💡 API网关 ⚡ 服务故障级联
雪崩
💡 熔断降级
⚡ 调用链复杂
出问题不知道哪
💡 链路追踪 ⚡ 配置散落
改一个配置重启10个服务
💡 配置中心
类比:从小作坊到连锁企业
单体 = 小作坊,老板什么都干 → 拆分 = 分成采购部/生产部/销售部 → 注册发现 = 公司通讯录 → 网关 = 前台接待 → 熔断 = 部门预算控制 → 链路追踪 = 工单追踪 → 配置中心 = 公司公告栏

① 因为:单体应用越来越大 → 所以服务拆分

❓ 问题:单体应用的3大痛点

1. 部署慢:改一行代码,整个项目重新编译打包部署(30分钟)

2. 扩展难:用户模块压力大,但必须把整个单体都扩展(浪费资源)

3. 技术栈绑定:想用Go写新功能?不行,整个项目都是Java

💡 所以:服务拆分——按业务领域拆成独立服务
单体应用
用户+订单+支付+库存
↓ 拆分
用户服务 订单服务 支付服务 库存服务
拆分原则(因为…所以):
因为要独立部署 → 所以按业务领域拆分(DDD)
因为要数据隔离 → 所以每个服务有自己的数据库
因为要团队自治 → 所以每个服务由一个小团队负责
⚡ 但是:拆分后服务地址怎么管理?

订单服务调用库存服务:http://192.168.1.10:8083/inventory/deduct → 库存服务扩容换了IP → 所有调用方都要改代码!

② 因为:地址硬编码 → 所以服务注册发现

❓ 问题:IP地址写死在代码里
// ❌ 硬编码IP:服务扩容/缩容/宕机→所有调用方都要改 @FeignClient(url = "http://192.168.1.10:8083") public interface InventoryClient { @PostMapping("/inventory/deduct") Result deduct(@RequestBody DeductRequest req); }
💡 所以:服务注册发现——每个服务启动时注册自己的地址
库存服务1 注册中心
(Nacos)
库存服务2
订单服务 从注册中心拉取
库存服务列表
负载均衡
选择一个实例
// ✅ Nacos 注册发现:用服务名代替IP @FeignClient(name = "inventory-service") // 用名字!不用IP public interface InventoryClient { @PostMapping("/inventory/deduct") Result deduct(@RequestBody DeductRequest req); } // Nacos 配置 spring.application.name=inventory-service spring.cloud.nacos.discovery.server-addr=nacos:8848
⚡ 但是:每个服务都有自己的地址,客户端怎么统一入口?

10个服务就有10个地址 → 前端要记住10个URL → 认证、限流、日志每个服务都要写 → 引出API网关

③ 因为:没有统一入口 → 所以API网关

❓ 问题:每个服务各自暴露,公共逻辑重复
💡 所以:API网关——所有请求统一入口
客户端 API网关
认证/限流/路由/日志
用户服务 订单服务 支付服务
// Spring Cloud Gateway 配置 spring: cloud: gateway: routes: - id: user-service uri: lb://user-service # 负载均衡 predicates: - Path=/api/user/** # /api/user/** → 用户服务 filters: - StripPrefix=1 # 去掉/api前缀 - name: RequestRateLimiter # 限流 args: redis-rate-limiter.replenishRate: 100 redis-rate-limiter.burstCapacity: 200 - id: order-service uri: lb://order-service predicates: - Path=/api/order/**

④ 因为:服务故障级联 → 所以熔断降级

❓ 问题:一个服务宕机 → 所有上游服务都卡死 → 雪崩
订单服务 支付服务 库存服务
💥宕机
支付服务线程全部阻塞等待库存响应 → 也挂了
订单服务线程全部阻塞等待支付响应 → 也挂了
💡 所以:熔断降级——因为一个服务挂不该拖垮整条链
// Sentinel 熔断降级 @SentinelResource(value = "deduct", blockHandler = "deductBlock", // 限流/熔断时走这里 fallback = "deductFallback") // 异常时走这里 public Result deduct(DeductRequest req) { return inventoryClient.deduct(req); } // 熔断后降级逻辑 public Result deductBlock(DeductRequest req, BlockException ex) { log.warn("库存服务被熔断,走降级逻辑"); return Result.fail("系统繁忙,请稍后重试"); } // 异常降级 public Result deductFallback(DeductRequest req, Throwable ex) { log.error("库存服务异常", ex); return Result.fail("服务暂时不可用"); } // 熔断规则配置 // 因为:连续N次失败 → 说明服务可能挂了 // 所以:直接熔断,不再调用,给服务恢复时间 // 熔断3种状态:关闭 → 打开(直接拒绝) → 半开(试探性放行)
类比:保险丝
家里某个电器短路 → 保险丝熔断 → 只影响那一路,不会整栋楼停电 → 修好了再合上保险丝

⑤ 因为:调用链复杂 → 所以链路追踪

❓ 问题:一个请求经过5个服务,出错了不知道哪个服务慢
💡 所以:链路追踪——每个请求带唯一traceId
// Sleuth + Zipkin // 因为:一个请求经过多个服务 // 所以:给每个请求一个唯一的traceId // 每经过一个服务,生成一个spanId // 请求链路: // [traceId: abc123] // ├─ Gateway (spanId: 1, 5ms) // ├─ OrderService (spanId: 2, 120ms) ← 慢! // │ ├─ UserService (spanId: 2.1, 10ms) // │ └─ InventoryService (spanId: 2.2, 105ms) ← 真正慢在这里! // └─ PaymentService (spanId: 3, 30ms) // Maven 依赖 // spring-cloud-starter-sleuth // spring-cloud-sleuth-zipkin // application.yml spring: sleuth: sampler: probability: 1.0 # 采样率(生产环境0.1即可) zipkin: base-url: http://zipkin:9411
类比:快递物流追踪
你寄了个快递,能查到:北京分拣中心(5min) → 上海中转站(2h) → 杭州派送点(30min) → 送达。链路追踪就是微服务的"物流追踪"。

⑥ 因为:配置散落 → 所以配置中心

❓ 问题:10个服务各自管理配置,改一个数据库密码要重启10个
💡 所以:配置中心——集中管理+动态刷新
// Nacos 配置中心 // 因为:配置散落在各服务 → 所以集中管理 // 因为:改配置要重启 → 所以支持动态刷新 // bootstrap.yml spring: cloud: nacos: config: server-addr: nacos:8848 file-extension: yaml shared-configs: # 共享配置 - data-id: common-db.yaml refresh: true # 支持动态刷新 // 业务代码中使用动态配置 @RefreshScope // 配置变更时自动刷新 @RestController public class OrderController { @Value("${order.max-amount:10000}") private BigDecimal maxAmount; // 配置中心改了自动生效 } // Nacos 控制台改配置 → 不用重启服务 → 立刻生效!

🎯 面试点映射:因果链视角

面试题因为…所以…
为什么要微服务因为单体部署慢/扩展难/技术栈绑定 → 所以按业务拆分
服务注册发现原理因为地址硬编码无法动态伸缩 → 所以服务启动时注册到中心,调用方从中心拉取
CAP与注册中心选型因为网络分区不可避免 → 所以Eureka选AP(可用),ZooKeeper选CP(一致)
API网关作用因为没有统一入口 → 所以网关统一路由/认证/限流/日志
熔断降级原理因为一个服务挂了会级联 → 所以熔断快速失败,降级返回兜底数据
链路追踪原理因为调用链复杂不知道哪里慢 → 所以每个请求带traceId追踪
配置中心因为配置散落改了要重启 → 所以集中管理+动态刷新

📌 因果链总结

单体
太大
服务
拆分
地址
硬编码
注册
发现
没有
统一入口
API
网关
故障
级联
熔断
降级
调用链
复杂
链路
追踪
配置
散落
配置
中心
全栈资料库 · 因果链板块 · 微服务因果链