系列导航:本文是《Spring框架第一性原理》系列的第5篇


引子:单体应用的困境

场景重现:一个电商系统的演进之路

让我们从一个真实的电商系统的成长历程说起。

第一阶段:创业初期(2015年)

团队规模:5人(2个后端、1个前端、1个产品、1个UI)

技术选型:单体架构 + Spring Boot

系统架构

┌─────────────────────────────────────┐
│      电商单体应用                    │
│  (monolithic-ecommerce-app)        │
├─────────────────────────────────────┤
│  用户模块  (UserModule)              │
│  商品模块  (ProductModule)           │
│  订单模块  (OrderModule)             │
│  库存模块  (InventoryModule)         │
│  支付模块  (PaymentModule)           │
│  物流模块  (LogisticsModule)         │
│  营销模块  (MarketingModule)         │
├─────────────────────────────────────┤
│  Spring Boot + MyBatis + MySQL      │
└─────────────────────────────────────┘
        ↓
    MySQL数据库

代码结构

ecommerce-app/
├── src/main/java/com/example/
│   ├── user/              # 用户模块
│   │   ├── UserController.java
│   │   ├── UserService.java
│   │   └── UserRepository.java
│   ├── product/           # 商品模块
│   │   ├── ProductController.java
│   │   ├── ProductService.java
│   │   └── ProductRepository.java
│   ├── order/             # 订单模块
│   │   ├── OrderController.java
│   │   ├── OrderService.java
│   │   └── OrderRepository.java
│   ├── inventory/         # 库存模块
│   ├── payment/           # 支付模块
│   ├── logistics/         # 物流模块
│   └── marketing/         # 营销模块
└── pom.xml

典型业务流程(创建订单)

@Service
public class OrderService {

    @Autowired
    private UserService userService;

    @Autowired
    private ProductService productService;

    @Autowired
    private InventoryService inventoryService;

    @Autowired
    private PaymentService paymentService;

    @Autowired
    private LogisticsService logisticsService;

    @Transactional
    public Order createOrder(OrderRequest request) {
        // 1. 验证用户
        User user = userService.getUser(request.getUserId());
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        // 2. 验证商品
        Product product = productService.getProduct(request.getProductId());
        if (product == null) {
            throw new BusinessException("商品不存在");
        }

        // 3. 扣减库存
        boolean stockSuccess = inventoryService.deduct(
            request.getProductId(),
            request.getQuantity()
        );
        if (!stockSuccess) {
            throw new BusinessException("库存不足");
        }

        // 4. 创建订单
        Order order = new Order();
        order.setUserId(request.getUserId());
        order.setProductId(request.getProductId());
        order.setQuantity(request.getQuantity());
        order.setAmount(product.getPrice() * request.getQuantity());
        order.setStatus("CREATED");
        orderRepository.save(order);

        // 5. 调用支付
        boolean paySuccess = paymentService.pay(order.getId(), order.getAmount());
        if (paySuccess) {
            order.setStatus("PAID");
            orderRepository.update(order);

            // 6. 创建物流订单
            logisticsService.createShipment(order.getId());
        }

        return order;
    }
}

这个阶段的特点

✅ 优势:
├─ 开发简单:所有代码在一个项目中,开发方便
├─ 部署简单:打包成一个JAR,部署一台服务器
├─ 调试简单:本地启动即可调试所有功能
├─ 事务简单:使用@Transactional保证一致性
└─ 性能不错:本地方法调用,无网络开销

❌ 没有明显问题:
├─ 用户量:日活1000+
├─ 并发量:QPS < 100
├─ 团队规模:5人
└─ 代码量:2万行

第二阶段:快速增长期(2016-2017年)

业务增长

  • 日活用户:1000 → 10万
  • 日订单量:100 → 5万
  • 团队规模:5人 → 30人(6个开发团队)

单体应用开始出现问题

问题1:部署困境
场景:营销团队开发了一个秒杀功能
需求:紧急上线(晚上8点)

部署流程(单体应用):
1. 营销团队提交代码
2. 合并到主分支
3. 整个应用重新编译
4. 停止现有服务
5. 部署新版本
6. 启动服务(需要5分钟)
7. 验证所有功能(需要30分钟)

问题:
❌ 秒杀功能上线,但影响了所有模块
❌ 部署期间,整个网站不可用(5分钟)
❌ 任何一个模块的bug,都会导致整个应用回滚
❌ 团队之间互相等待(营销等订单、订单等支付)

数据:
├─ 部署频率:每周1次(不敢频繁部署)
├─ 部署时长:30-60分钟
├─ 部署风险:高(牵一发动全身)
└─ 回滚成本:高(整个应用回滚)
问题2:性能瓶颈
场景:618大促,流量激增

系统负载分析:
┌────────────────────────────────────┐
│  模块        │  QPS  │  资源占用   │
├────────────────────────────────────┤
│  商品浏览    │  10000 │  CPU 60%   │  ← 高并发
│  订单创建    │  500   │  CPU 20%   │
│  支付处理    │  300   │  CPU 10%   │
│  物流查询    │  200   │  CPU 5%    │
│  用户中心    │  100   │  CPU 5%    │
└────────────────────────────────────┘

问题:
❌ 商品浏览是高并发场景,需要大量机器
❌ 但订单、支付是低并发,不需要那么多资源
❌ 单体应用无法独立扩展某个模块
❌ 只能整体扩展(浪费资源)

扩展方案(单体应用):
部署10台服务器 × 4核8G = 40核80G
实际需求:
├─ 商品模块:30核 60G(占75%)
├─ 订单模块:5核 10G
├─ 支付模块:3核 6G
└─ 其他模块:2核 4G

浪费:
├─ 订单、支付、物流等模块资源严重浪费
└─ 无法根据模块单独扩展
问题3:技术栈绑定
场景:营销团队想用Python开发推荐算法服务

问题:
❌ 单体应用是Java技术栈
❌ 无法在单体应用中集成Python代码
❌ 只能用Java重写(效率低、效果差)
❌ 或者放弃(错过商机)

团队诉求:
├─ 营销团队:想用Python做推荐算法
├─ 数据团队:想用Go做数据采集
├─ 搜索团队:想用Elasticsearch
└─ 但单体应用无法支持
问题4:代码管理混乱
场景:30人的团队协作开发

代码冲突:
├─ 6个团队共用一个Git仓库
├─ 每天都有代码冲突(合并地狱)
├─ 一个团队的bug影响其他团队
└─ 测试环境经常被其他团队"搞坏"

编译缓慢:
├─ 代码量:2万行 → 20万行
├─ 编译时间:10秒 → 5分钟
├─ 启动时间:30秒 → 3分钟
└─ 开发效率严重下降

依赖复杂:
├─ 订单模块依赖支付模块
├─ 支付模块依赖订单模块(循环依赖)
├─ 营销模块依赖所有模块
└─ 改一个模块,影响所有模块
问题5:数据库瓶颈
场景:所有模块共用一个MySQL数据库

数据库压力:
┌────────────────────────────────────┐
│  表           │  数据量  │  QPS    │
├────────────────────────────────────┤
│  products     │  100万   │  10000  │  ← 高并发
│  orders       │  500万   │  500    │
│  users        │  50万    │  300    │
│  payments     │  500万   │  300    │
│  logistics    │  500万   │  200    │
└────────────────────────────────────┘
总QPS:11300(单库无法支撑)

问题:
❌ 所有表在一个数据库,无法独立扩展
❌ 商品表的高并发拖垮整个数据库
❌ 订单表数据量大,慢查询影响其他表
❌ 无法做垂直拆分(按业务拆库)

单体应用的本质问题

单体应用的五大本质问题:

1. 耦合度高
   └─ 所有模块在一个进程中
      └─ 任何模块的bug都会影响整个应用
      └─ 任何模块的升级都需要重新部署整个应用

2. 扩展性差
   └─ 无法独立扩展某个模块
      └─ 只能整体扩展(资源浪费)
      └─ 无法根据流量特点灵活扩展

3. 技术栈单一
   └─ 整个应用必须使用同一技术栈
      └─ 无法针对不同场景选择最优技术
      └─ 无法引入新技术(风险高)

4. 团队协作困难
   └─ 所有团队共用一个代码库
      └─ 代码冲突频繁
      └─ 编译缓慢(代码量大)
      └─ 测试环境互相干扰

5. 部署风险高
   └─ 小改动需要重新部署整个应用
      └─ 部署时间长(编译+启动)
      └─ 部署风险高(牵一发动全身)
      └─ 回滚成本高(整个应用回滚)

微服务架构:问题的解决方案

什么是微服务?

微服务的定义:
  将一个单体应用拆分成多个小型、独立的服务
  每个服务:
    ├─ 运行在自己的进程中
    ├─ 使用轻量级通信机制(通常是HTTP/REST或RPC)
    ├─ 围绕业务能力构建
    ├─ 可以独立部署
    ├─ 可以使用不同的技术栈
    └─ 由独立的团队维护

微服务 vs 单体应用:
单体应用 = 1个进程 × 所有功能
微服务 = N个进程 × 单一职责

类比:
单体应用 = 瑞士军刀(集成所有功能,但笨重)
微服务 = 工具箱(每个工具专注一件事,灵活组合)

电商系统的微服务拆分

拆分原则:按业务能力划分

拆分策略(DDD领域驱动设计):
1. 识别业务领域
   ├─ 用户域:用户注册、登录、个人信息
   ├─ 商品域:商品管理、分类、搜索
   ├─ 订单域:订单创建、支付、取消
   ├─ 库存域:库存管理、扣减、回滚
   ├─ 支付域:支付处理、退款
   ├─ 物流域:物流跟踪、配送
   └─ 营销域:优惠券、促销活动

2. 定义服务边界
   ├─ 每个服务独立部署
   ├─ 每个服务有自己的数据库
   ├─ 服务间通过API通信
   └─ 服务内部高内聚

3. 识别依赖关系
   ├─ 订单服务依赖:用户、商品、库存、支付
   ├─ 支付服务依赖:订单
   └─ 避免循环依赖

微服务架构图

┌─────────────────────────────────────────────────────────┐
│                     API网关 (Gateway)                     │
│                  统一入口、路由、鉴权                      │
└─────────────────────────────────────────────────────────┘
              │
    ┌─────────┼─────────────────────────────────┐
    │         │                                 │
┌───▼───┐ ┌──▼────┐ ┌──────┐ ┌───────┐ ┌──────┐ ┌───────┐
│ 用户   │ │ 商品  │ │ 订单  │ │ 库存  │ │ 支付  │ │ 物流  │
│服务   │ │ 服务  │ │ 服务  │ │ 服务  │ │ 服务  │ │ 服务  │
└───┬───┘ └──┬────┘ └──┬───┘ └───┬───┘ └──┬───┘ └───┬───┘
    │        │         │         │        │        │
┌───▼───┐ ┌──▼────┐ ┌──▼───┐ ┌──▼────┐ ┌──▼───┐ ┌──▼────┐
│UserDB │ │ProdDB │ │OrderDB│ │InvDB  │ │PayDB │ │LogDB  │
└───────┘ └───────┘ └──────┘ └───────┘ └──────┘ └───────┘

                ┌────────────────────┐
                │  注册中心 (Nacos)   │
                │  服务发现、配置管理  │
                └────────────────────┘

订单服务的微服务实现

// 订单服务(独立应用)
@SpringBootApplication
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

@RestController
@RequestMapping("/orders")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @PostMapping
    public Order createOrder(@RequestBody OrderRequest request) {
        return orderService.createOrder(request);
    }
}

@Service
public class OrderService {

    @Autowired
    private UserServiceClient userServiceClient;  // 远程调用用户服务

    @Autowired
    private ProductServiceClient productServiceClient;  // 远程调用商品服务

    @Autowired
    private InventoryServiceClient inventoryServiceClient;  // 远程调用库存服务

    @Autowired
    private PaymentServiceClient paymentServiceClient;  // 远程调用支付服务

    public Order createOrder(OrderRequest request) {
        // 1. 远程调用用户服务验证用户
        User user = userServiceClient.getUser(request.getUserId());
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        // 2. 远程调用商品服务验证商品
        Product product = productServiceClient.getProduct(request.getProductId());
        if (product == null) {
            throw new BusinessException("商品不存在");
        }

        // 3. 远程调用库存服务扣减库存
        boolean stockSuccess = inventoryServiceClient.deduct(
            request.getProductId(),
            request.getQuantity()
        );
        if (!stockSuccess) {
            throw new BusinessException("库存不足");
        }

        // 4. 创建订单(本地数据库)
        Order order = new Order();
        order.setUserId(request.getUserId());
        order.setProductId(request.getProductId());
        order.setQuantity(request.getQuantity());
        order.setAmount(product.getPrice() * request.getQuantity());
        order.setStatus("CREATED");
        orderRepository.save(order);

        // 5. 远程调用支付服务
        boolean paySuccess = paymentServiceClient.pay(order.getId(), order.getAmount());
        if (paySuccess) {
            order.setStatus("PAID");
            orderRepository.update(order);
        }

        return order;
    }
}

// 用户服务客户端(Feign声明式HTTP客户端)
@FeignClient(name = "user-service")
public interface UserServiceClient {
    @GetMapping("/users/{id}")
    User getUser(@PathVariable("id") Long id);
}

// 商品服务客户端
@FeignClient(name = "product-service")
public interface ProductServiceClient {
    @GetMapping("/products/{id}")
    Product getProduct(@PathVariable("id") Long id);
}

// 库存服务客户端
@FeignClient(name = "inventory-service")
public interface InventoryServiceClient {
    @PostMapping("/inventory/deduct")
    boolean deduct(@RequestParam("productId") Long productId,
                   @RequestParam("quantity") int quantity);
}

// 支付服务客户端
@FeignClient(name = "payment-service")
public interface PaymentServiceClient {
    @PostMapping("/payments/pay")
    boolean pay(@RequestParam("orderId") Long orderId,
                @RequestParam("amount") BigDecimal amount);
}

微服务带来的好处

对比:单体应用 vs 微服务

1. 部署独立性
   单体:修改营销模块 → 重新部署整个应用 → 影响所有用户
   微服务:修改营销服务 → 只部署营销服务 → 只影响营销功能

2. 扩展灵活性
   单体:商品模块高并发 → 扩展整个应用 → 资源浪费
   微服务:商品服务高并发 → 只扩展商品服务 → 资源优化

3. 技术栈自由
   单体:整个应用必须用Java
   微服务:商品服务用Java、推荐服务用Python、搜索用Go

4. 团队独立性
   单体:6个团队共用一个代码库 → 代码冲突频繁
   微服务:6个团队各自维护自己的服务 → 独立开发、独立部署

5. 故障隔离
   单体:支付模块bug → 整个应用崩溃 → 所有功能不可用
   微服务:支付服务bug → 只影响支付 → 其他功能正常

微服务的五大核心问题

虽然微服务解决了单体应用的问题,但也引入了新的复杂度。

问题1:服务发现(Service Discovery)

问题场景

场景:订单服务需要调用用户服务

单体应用:
orderService.userService.getUser(userId);
// 本地方法调用,直接调用即可

微服务:
userServiceClient.getUser(userId);
// 问题:用户服务的IP地址和端口是什么?

困境:
├─ 用户服务可能部署在多台服务器(负载均衡)
│   ├─ 192.168.1.10:8080
│   ├─ 192.168.1.11:8080
│   └─ 192.168.1.12:8080
├─ 服务器可能动态增减(弹性伸缩)
│   └─ 新增服务器、下线服务器
├─ 服务器可能故障(健康检查)
│   └─ 某台服务器宕机,不能再调用
└─ 如何知道可用的服务器列表?

方案1:硬编码(不推荐)

// 硬编码服务地址
@Service
public class OrderService {
    public Order createOrder(OrderRequest request) {
        // 问题:硬编码IP地址
        String userServiceUrl = "http://192.168.1.10:8080";
        User user = restTemplate.getForObject(
            userServiceUrl + "/users/" + request.getUserId(),
            User.class
        );
        // ...
    }
}

问题
 IP地址硬编码修改需要重新发布
 无法负载均衡只能调用一台服务器
 无法感知服务器上下线
 无法做故障转移

方案2:配置文件(部分解决)

# application.yml
services:
  user-service:
    urls:
      - http://192.168.1.10:8080
      - http://192.168.1.11:8080
      - http://192.168.1.12:8080
@Service
public class OrderService {

    @Value("${services.user-service.urls}")
    private List<String> userServiceUrls;

    private int currentIndex = 0;

    public Order createOrder(OrderRequest request) {
        // 简单的轮询负载均衡
        String url = userServiceUrls.get(currentIndex % userServiceUrls.size());
        currentIndex++;

        User user = restTemplate.getForObject(
            url + "/users/" + request.getUserId(),
            User.class
        );
        // ...
    }
}

改进
 IP地址外部化配置
 支持负载均衡轮询

仍存在问题
 修改服务器需要重启应用
 无法感知服务器健康状态
 无法自动剔除故障服务器

方案3:服务注册中心(完美解决)

核心思想:引入第三方组件(注册中心),统一管理所有服务的地址

服务注册中心(如Nacos、Eureka、Consul):

工作流程:
1. 服务启动时,向注册中心注册自己的地址
   用户服务A:192.168.1.10:8080 → Nacos
   用户服务B:192.168.1.11:8080 → Nacos
   用户服务C:192.168.1.12:8080 → Nacos

2. 服务定期发送心跳(健康检查)
   用户服务A → Nacos: "我还活着"(每10秒)

3. 注册中心检测服务健康状态
   ├─ 收到心跳 → 标记为健康
   └─ 超过30秒未收到心跳 → 标记为下线

4. 调用方从注册中心获取服务列表
   订单服务 → Nacos: "给我用户服务的地址列表"
   Nacos → 订单服务: [192.168.1.10:8080, 192.168.1.11:8080]

5. 调用方选择一个服务调用(负载均衡)
   订单服务 → 用户服务A(轮询、随机、加权等策略)

6. 服务下线时,从注册中心注销
   用户服务A下线 → Nacos移除192.168.1.10:8080

Spring Cloud + Nacos实现

<!-- 1. 引入Nacos服务发现依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
# 2. 配置Nacos地址
spring:
  application:
    name: order-service  # 服务名称
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848  # Nacos地址
        namespace: dev  # 命名空间(环境隔离)
        group: DEFAULT_GROUP  # 分组
// 3. 启动类添加注解
@SpringBootApplication
@EnableDiscoveryClient  // 启用服务发现
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

// 4. 使用Feign客户端(自动服务发现 + 负载均衡)
@FeignClient(name = "user-service")  // 服务名称(不是IP地址)
public interface UserServiceClient {
    @GetMapping("/users/{id}")
    User getUser(@PathVariable("id") Long id);
}

// 5. 业务代码(无需关心服务地址)
@Service
public class OrderService {

    @Autowired
    private UserServiceClient userServiceClient;

    public Order createOrder(OrderRequest request) {
        // 自动从Nacos获取user-service的地址列表
        // 自动负载均衡选择一个服务调用
        User user = userServiceClient.getUser(request.getUserId());
        // ...
    }
}

工作原理

启动流程:
1. 订单服务启动
2. 订单服务注册到Nacos(名称:order-service,地址:192.168.1.20:8081)
3. 用户服务启动
4. 用户服务注册到Nacos(名称:user-service,地址:192.168.1.10:8080)

调用流程:
1. 订单服务调用userServiceClient.getUser(123)
2. Feign拦截调用,向Nacos查询user-service的地址列表
3. Nacos返回:[192.168.1.10:8080, 192.168.1.11:8080, 192.168.1.12:8080]
4. 负载均衡器选择一个地址(例如:192.168.1.10:8080)
5. 发起HTTP请求:GET http://192.168.1.10:8080/users/123
6. 用户服务返回结果
7. Feign返回给订单服务

故障转移:
1. 用户服务A(192.168.1.10:8080)宕机
2. Nacos检测到心跳超时(30秒)
3. Nacos从服务列表移除192.168.1.10:8080
4. 订单服务下次调用时,只会获取到[192.168.1.11:8080, 192.168.1.12:8080]
5. 自动故障转移,不会调用到故障服务器

问题2:配置管理(Configuration Management)

问题场景

场景:订单服务需要连接MySQL数据库

单体应用:
application.yml(1个配置文件)
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: 123456

微服务:
订单服务(application.yml)
商品服务(application.yml)
用户服务(application.yml)
支付服务(application.yml)
库存服务(application.yml)
物流服务(application.yml)
... 共10个服务 × 3个环境(开发、测试、生产)= 30个配置文件

困境:
├─ 配置分散:30个配置文件,管理复杂
├─ 配置重复:数据库连接、Redis地址等配置在多个服务重复
├─ 配置不一致:开发环境配置和生产环境不一致
├─ 配置修改困难:修改Redis地址,需要改10个服务的配置文件
└─ 配置变更需要重启:修改配置后需要重启服务(停机)

方案1:配置文件(传统方式)

问题示例:修改Redis地址

步骤:
1. 修改订单服务的application.yml
2. 修改商品服务的application.yml
3. 修改用户服务的application.yml
... 修改10个服务的配置文件
4. 重启所有服务(停机10-30分钟)

问题:
❌ 修改繁琐:需要改10个配置文件
❌ 容易遗漏:某个服务忘记改,导致连接失败
❌ 需要重启:配置修改后必须重启服务
❌ 无版本控制:无法回滚到之前的配置
❌ 无权限控制:任何人都可以修改配置

方案2:配置中心(完美解决)

核心思想:引入配置中心(如Nacos、Apollo、Spring Cloud Config),集中管理所有服务的配置

配置中心(Nacos Config):

工作流程:
1. 配置存储在Nacos
   ├─ 公共配置(common):所有服务共用
   │   ├─ Redis地址:redis://localhost:6379
   │   ├─ 日志级别:INFO
   │   └─ 监控地址:http://monitor:9090
   └─ 服务专属配置(order-service):
       ├─ 数据库地址:jdbc:mysql://localhost:3306/order_db
       └─ 支付超时时间:30秒

2. 服务启动时,从Nacos加载配置
   订单服务启动 → Nacos获取配置 → 应用配置 → 启动完成

3. 配置动态刷新(无需重启)
   ├─ 管理员在Nacos修改Redis地址
   ├─ Nacos推送配置变更通知
   ├─ 订单服务收到通知
   ├─ 订单服务刷新配置
   └─ 新的Redis地址生效(无需重启)

4. 配置版本管理
   ├─ 每次修改配置,Nacos记录版本
   ├─ 支持回滚到历史版本
   └─ 支持灰度发布(部分服务使用新配置)

5. 权限控制
   ├─ 开发人员:只能读取配置
   ├─ 运维人员:可以修改配置
   └─ 审计日志:记录谁在什么时候修改了什么配置

Spring Cloud + Nacos Config实现

<!-- 1. 引入Nacos配置管理依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
# 2. bootstrap.yml(优先级高于application.yml)
spring:
  application:
    name: order-service
  cloud:
    nacos:
      config:
        server-addr: localhost:8848  # Nacos地址
        namespace: dev  # 命名空间
        group: DEFAULT_GROUP  # 分组
        file-extension: yaml  # 配置文件格式
        shared-configs:  # 共享配置
          - data-id: common.yaml  # 公共配置
            refresh: true  # 支持动态刷新
// 3. 使用@RefreshScope支持动态刷新
@RestController
@RefreshScope  // 配置变更时自动刷新此Bean
public class OrderController {

    @Value("${redis.host}")
    private String redisHost;  // 从Nacos读取配置

    @Value("${order.timeout}")
    private int orderTimeout;

    @GetMapping("/config")
    public Map<String, Object> getConfig() {
        Map<String, Object> config = new HashMap<>();
        config.put("redisHost", redisHost);
        config.put("orderTimeout", orderTimeout);
        return config;
    }
}
# 4. 在Nacos创建配置文件(通过Web UI)
# Data ID: order-service.yaml
# Group: DEFAULT_GROUP
# 内容:
server:
  port: 8081

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/order_db
    username: root
    password: ${DB_PASSWORD}  # 支持环境变量

redis:
  host: localhost
  port: 6379

order:
  timeout: 30

# Data ID: common.yaml(公共配置)
# 内容:
logging:
  level:
    root: INFO
    com.example: DEBUG

management:
  endpoints:
    web:
      exposure:
        include: "*"

配置变更流程

场景:修改Redis地址(从localhost改为redis-server)

传统方式:
1. 修改10个服务的application.yml
2. 重启10个服务
3. 总耗时:30分钟
4. 停机时间:30分钟

Nacos Config方式:
1. 在Nacos Web UI修改common.yaml
   redis.host: localhost → redis-server
2. 点击"发布"按钮
3. Nacos推送配置变更通知给所有订阅此配置的服务
4. 所有服务自动刷新配置(@RefreshScope)
5. 新的Redis地址生效
6. 总耗时:10秒
7. 停机时间:0秒

对比:
✅ 修改集中:只需在Nacos改1次
✅ 无需重启:配置动态刷新
✅ 生效迅速:10秒内生效
✅ 零停机:不影响服务运行

问题3:负载均衡(Load Balancing)

问题场景

场景:用户服务部署了3台服务器

用户服务实例:
├─ 实例A:192.168.1.10:8080(CPU 80%)
├─ 实例B:192.168.1.11:8080(CPU 20%)
└─ 实例C:192.168.1.12:8080(CPU 30%)

问题:
订单服务调用用户服务时,应该选择哪个实例?
├─ 如果总是调用实例A → 实例A负载过高,可能崩溃
├─ 如果随机调用 → 可能仍然不均衡
└─ 如何做到负载均衡?

负载均衡策略

// Spring Cloud LoadBalancer提供的负载均衡策略

// 1. 轮询策略(Round Robin)- 默认
public class RoundRobinLoadBalancer {
    private AtomicInteger position = new AtomicInteger(0);

    public ServiceInstance choose(List<ServiceInstance> instances) {
        int pos = position.incrementAndGet() % instances.size();
        return instances.get(pos);
    }
}
// 调用顺序:A → B → C → A → B → C ...
// 优点:简单、公平
// 缺点:不考虑服务器性能差异

// 2. 随机策略(Random)
public class RandomLoadBalancer {
    private Random random = new Random();

    public ServiceInstance choose(List<ServiceInstance> instances) {
        int index = random.nextInt(instances.size());
        return instances.get(index);
    }
}
// 调用顺序:随机选择
// 优点:简单、分散
// 缺点:可能不均衡

// 3. 加权轮询策略(Weighted Round Robin)
public class WeightedRoundRobinLoadBalancer {
    public ServiceInstance choose(List<ServiceInstance> instances) {
        // 根据服务器性能设置权重
        // 实例A:权重1(性能差)
        // 实例B:权重2(性能好)
        // 实例C:权重3(性能最好)
        // 调用顺序:C → C → C → B → B → A
    }
}
// 优点:考虑服务器性能差异
// 缺点:需要手动配置权重

// 4. 最少活跃连接策略(Least Active)
public class LeastActiveLoadBalancer {
    public ServiceInstance choose(List<ServiceInstance> instances) {
        // 选择当前活跃连接数最少的服务器
        // 实例A:10个活跃连接
        // 实例B:5个活跃连接 ← 选择B
        // 实例C:8个活跃连接
    }
}
// 优点:动态感知服务器负载
// 缺点:需要额外维护连接数信息

// 5. 一致性哈希策略(Consistent Hash)
public class ConsistentHashLoadBalancer {
    public ServiceInstance choose(List<ServiceInstance> instances, Object key) {
        // 根据key(如用户ID)计算哈希值
        // 相同key总是路由到同一服务器
        // 用户123 → 实例A
        // 用户456 → 实例B
    }
}
// 优点:相同key总是路由到同一服务器(适合会话保持)
// 缺点:服务器增减时会导致部分key路由变化

Spring Cloud LoadBalancer配置

# application.yml
spring:
  cloud:
    loadbalancer:
      retry:
        enabled: true  # 启用重试
        max-retries: 3  # 最大重试次数
      ribbon:
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule  # 轮询策略
// 自定义负载均衡策略
@Configuration
public class LoadBalancerConfig {

    @Bean
    public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(
            Environment environment,
            LoadBalancerClientFactory loadBalancerClientFactory) {

        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RandomLoadBalancer(
            loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
            name
        );
    }
}

问题4:熔断降级(Circuit Breaker & Fallback)

问题场景

场景:订单服务调用支付服务

正常情况:
订单服务 → 支付服务(100ms响应)→ 返回成功

异常情况1:支付服务响应慢
订单服务 → 支付服务(5秒响应)→ 订单服务等待5秒 → 用户体验差

异常情况2:支付服务宕机
订单服务 → 支付服务(连接超时30秒)→ 订单服务等待30秒 → 用户体验极差

雪崩效应:
1. 支付服务响应慢
2. 订单服务线程阻塞(等待支付服务响应)
3. 订单服务线程池耗尽(所有线程都在等待)
4. 订单服务无法响应新请求
5. 订单服务崩溃
6. 依赖订单服务的服务也崩溃
7. 整个系统雪崩

问题本质:
服务之间的依赖关系形成了脆弱的链条
任何一个环节出问题,都可能导致整个链条崩溃

解决方案:熔断降级

熔断器(Circuit Breaker)原理

熔断器三种状态:

1. 关闭状态(Closed)- 正常状态
   ├─ 所有请求正常通过
   ├─ 统计错误率
   └─ 错误率超过阈值(如50%)→ 进入打开状态

2. 打开状态(Open)- 熔断状态
   ├─ 所有请求直接失败(不调用远程服务)
   ├─ 快速失败(不等待超时)
   ├─ 返回降级结果
   └─ 等待一段时间(如60秒)→ 进入半开状态

3. 半开状态(Half-Open)- 尝试恢复
   ├─ 允许少量请求通过
   ├─ 如果请求成功 → 进入关闭状态(恢复正常)
   └─ 如果请求失败 → 进入打开状态(继续熔断)

示例:
时间   状态      请求1  请求2  请求3  请求4  请求5
10:00  关闭      ✅     ✅     ✅     ✅     ✅
10:01  关闭      ❌     ❌     ❌     ✅     ✅   错误率40%(未达阈值)
10:02  关闭      ❌     ❌     ❌     ❌     ❌   错误率100%(超过阈值)
10:02  打开      ⚡     ⚡     ⚡     ⚡     ⚡   所有请求直接失败(快速失败)
11:02  半开      ✅     ✅     ❌     -      -    允许3个请求通过
11:02  打开      ⚡     ⚡     ⚡     ⚡     ⚡   仍有失败,继续熔断
12:02  半开      ✅     ✅     ✅     -      -    3个请求全部成功
12:02  关闭      ✅     ✅     ✅     ✅     ✅   恢复正常

降级(Fallback)

降级的本质:当服务不可用时,返回一个默认值,而不是让用户等待或报错

示例:订单服务调用支付服务
正常:
订单服务 → 支付服务 → 返回支付结果(成功/失败)

支付服务不可用(熔断):
订单服务 → 熔断器(直接失败)→ 降级逻辑 → 返回默认值("支付服务暂时不可用,请稍后重试")

降级策略:
1. 返回默认值
   ├─ 支付服务:返回"支付失败,请稍后重试"
   ├─ 推荐服务:返回热门商品列表(而不是个性化推荐)
   └─ 评论服务:返回空列表(而不是报错)

2. 返回缓存数据
   ├─ 商品服务:返回缓存的商品信息
   └─ 库存服务:返回缓存的库存数量

3. 降级到简化逻辑
   ├─ 订单服务:跳过积分计算、优惠券验证等非核心逻辑
   └─ 用户服务:跳过会员等级计算

Sentinel实现熔断降级

<!-- 1. 引入Sentinel依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
# 2. 配置Sentinel
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080  # Sentinel控制台地址
        port: 8719  # 与控制台通信的端口
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: sentinel-rules
            groupId: DEFAULT_GROUP
            rule-type: flow  # 流控规则
// 3. 定义降级逻辑
@Service
public class OrderService {

    @Autowired
    private PaymentServiceClient paymentServiceClient;

    @SentinelResource(
        value = "createOrder",  // 资源名称
        fallback = "createOrderFallback",  // 降级方法
        blockHandler = "createOrderBlockHandler"  // 熔断方法
    )
    public Order createOrder(OrderRequest request) {
        // 正常业务逻辑
        // ...

        // 调用支付服务
        boolean paySuccess = paymentServiceClient.pay(order.getId(), order.getAmount());

        // ...
    }

    // 降级方法(服务异常时调用)
    public Order createOrderFallback(OrderRequest request, Throwable ex) {
        log.error("订单创建失败,进入降级逻辑", ex);

        // 返回默认订单(状态为"待支付")
        Order order = new Order();
        order.setStatus("PENDING_PAYMENT");
        order.setMessage("支付服务暂时不可用,请稍后重试");
        return order;
    }

    // 熔断方法(触发限流/熔断时调用)
    public Order createOrderBlockHandler(OrderRequest request, BlockException ex) {
        log.warn("订单创建被限流/熔断", ex);

        // 返回提示信息
        Order order = new Order();
        order.setStatus("BLOCKED");
        order.setMessage("当前订单量过大,请稍后重试");
        return order;
    }
}
// 4. Feign集成Sentinel(自动熔断)
@FeignClient(
    name = "payment-service",
    fallback = PaymentServiceFallback.class  // 降级实现类
)
public interface PaymentServiceClient {
    @PostMapping("/payments/pay")
    boolean pay(@RequestParam("orderId") Long orderId,
                @RequestParam("amount") BigDecimal amount);
}

// 降级实现类
@Component
public class PaymentServiceFallback implements PaymentServiceClient {

    @Override
    public boolean pay(Long orderId, BigDecimal amount) {
        log.warn("支付服务不可用,返回降级结果");
        // 返回失败(触发重试或人工处理)
        return false;
    }
}
// 5. 在Sentinel控制台配置熔断规则(通过Web UI或代码)
// 规则示例:
DegradeRule rule = new DegradeRule();
rule.setResource("createOrder");  // 资源名称
rule.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType());  // 按错误率熔断
rule.setCount(0.5);  // 错误率阈值50%
rule.setTimeWindow(60);  // 熔断时长60秒
rule.setMinRequestAmount(10);  // 最小请求数(请求数少于10不熔断)
rule.setStatIntervalMs(10000);  // 统计时长10秒

DegradeRuleManager.loadRules(Collections.singletonList(rule));

熔断降级效果

场景:支付服务宕机

没有熔断降级:
10:00 - 订单1 → 支付服务(超时30秒)→ 失败
10:00 - 订单2 → 支付服务(超时30秒)→ 失败
10:00 - 订单3 → 支付服务(超时30秒)→ 失败
...
10:05 - 订单服务线程池耗尽 → 订单服务崩溃

结果:
❌ 用户等待时间长(30秒)
❌ 大量线程阻塞
❌ 订单服务崩溃

有熔断降级:
10:00 - 订单1 → 支付服务(超时30秒)→ 失败
10:00 - 订单2 → 支付服务(超时30秒)→ 失败
10:00 - 订单3 → 支付服务(超时30秒)→ 失败
10:00 - 错误率达到50%,触发熔断
10:00 - 订单4 → 熔断器(快速失败100ms)→ 降级逻辑 → 返回"支付服务暂时不可用"
10:00 - 订单5 → 熔断器(快速失败100ms)→ 降级逻辑 → 返回"支付服务暂时不可用"
...
11:00 - 熔断器进入半开状态,尝试恢复

结果:
✅ 用户等待时间短(100ms)
✅ 没有线程阻塞
✅ 订单服务正常运行
✅ 给用户友好提示

问题5:链路追踪(Distributed Tracing)

问题场景

场景:用户反馈"下单很慢"

单体应用:
├─ 查看日志:[OrderService] createOrder executed in 5000ms
├─ 找到慢的方法:inventoryService.deduct()耗时4800ms
└─ 优化库存扣减逻辑

微服务:
用户请求 → API网关 → 订单服务 → 用户服务 → 商品服务 → 库存服务 → 支付服务
                           ↑           ↑           ↑           ↑
                         100ms       200ms       300ms      4800ms(慢)

问题:
❌ 如何知道整个调用链路?
❌ 如何知道每个服务的耗时?
❌ 如何定位慢在哪个服务?
❌ 如何关联不同服务的日志?

解决方案:分布式链路追踪

核心概念

1. Trace(跟踪):一次完整的请求链路
   用户请求 → API网关 → 订单服务 → 用户服务 → 商品服务 → 库存服务 → 支付服务
   └─ TraceID: abc123(唯一标识这次请求)

2. Span(跨度):链路中的一个节点
   ├─ Span1: API网关(耗时10ms)
   ├─ Span2: 订单服务(耗时5200ms)
   │   ├─ Span3: 调用用户服务(耗时100ms)
   │   ├─ Span4: 调用商品服务(耗时200ms)
   │   ├─ Span5: 调用库存服务(耗时4800ms)← 找到瓶颈
   │   └─ Span6: 调用支付服务(耗时100ms)
   └─ 每个Span包含:
       ├─ SpanID: 唯一标识
       ├─ ParentSpanID: 父Span的ID
       ├─ ServiceName: 服务名称
       ├─ OperationName: 操作名称
       ├─ StartTime: 开始时间
       ├─ Duration: 持续时间
       └─ Tags: 标签(HTTP方法、URL、状态码等)

3. 日志关联
   订单服务日志:[TraceID=abc123] [SpanID=span2] OrderService.createOrder
   用户服务日志:[TraceID=abc123] [SpanID=span3] UserService.getUser
   库存服务日志:[TraceID=abc123] [SpanID=span5] InventoryService.deduct
   └─ 通过TraceID关联所有服务的日志

SkyWalking实现链路追踪

<!-- 1. 引入SkyWalking依赖 -->
<dependency>
    <groupId>org.apache.skywalking</groupId>
    <artifactId>apm-toolkit-trace</artifactId>
    <version>8.12.0</version>
</dependency>
# 2. 配置SkyWalking Agent(JVM启动参数)
java -javaagent:/path/to/skywalking-agent.jar \
     -Dskywalking.agent.service_name=order-service \
     -Dskywalking.collector.backend_service=localhost:11800 \
     -jar order-service.jar
// 3. 业务代码(无侵入,自动追踪)
@Service
public class OrderService {

    @Autowired
    private UserServiceClient userServiceClient;

    @Autowired
    private ProductServiceClient productServiceClient;

    @Autowired
    private InventoryServiceClient inventoryServiceClient;

    public Order createOrder(OrderRequest request) {
        // SkyWalking自动追踪所有方法调用
        // 无需修改代码

        User user = userServiceClient.getUser(request.getUserId());
        Product product = productServiceClient.getProduct(request.getProductId());
        boolean stockSuccess = inventoryServiceClient.deduct(request.getProductId(), request.getQuantity());

        // ...
    }
}

// 4. 自定义追踪(可选)
@Service
public class OrderService {

    @Trace  // 手动标记需要追踪的方法
    @Tags({
        @Tag(key = "orderId", value = "arg[0].orderId"),
        @Tag(key = "userId", value = "arg[0].userId")
    })
    public Order createOrder(OrderRequest request) {
        // 添加自定义标签
        TraceContext.putCorrelation("orderType", request.getType());

        // 业务逻辑
        // ...
    }
}

SkyWalking UI界面

1. 拓扑图(Topology):
   显示所有服务的调用关系

   [API网关] → [订单服务] → [用户服务]
                    ↓
              [商品服务]
                    ↓
              [库存服务]
                    ↓
              [支付服务]

2. 追踪详情(Trace):
   TraceID: abc123
   总耗时: 5210ms

   └─ API网关 (10ms)
      └─ 订单服务 (5200ms)
         ├─ 用户服务 (100ms)  ✅ 快
         ├─ 商品服务 (200ms)  ✅ 快
         ├─ 库存服务 (4800ms) ❌ 慢(瓶颈)
         └─ 支付服务 (100ms)  ✅ 快

3. 日志查询:
   输入TraceID: abc123
   返回所有相关日志:
   ├─ [订单服务] OrderService.createOrder started
   ├─ [用户服务] UserService.getUser userId=123
   ├─ [商品服务] ProductService.getProduct productId=456
   ├─ [库存服务] InventoryService.deduct slow query detected
   └─ [支付服务] PaymentService.pay orderId=789

4. 性能分析:
   ├─ 平均响应时间:200ms
   ├─ P95响应时间:500ms
   ├─ P99响应时间:2000ms
   └─ 错误率:0.1%

5. 慢请求分析:
   ├─ 最慢的10个请求
   ├─ 慢在哪个服务
   └─ 慢在哪个方法

微服务拆分的第一性原理

康威定律(Conway’s Law)

定律内容:
"组织架构决定系统架构"
"System design is a reflection of organizational structure"

含义:
├─ 如果一个组织有4个团队开发编译器
├─ 那么最终会得到一个4-pass编译器
└─ 因为每个团队负责一个pass

应用到微服务:
├─ 团队结构 → 服务边界
├─ 如果有用户团队、商品团队、订单团队
├─ 那么应该拆分为用户服务、商品服务、订单服务
└─ 每个团队独立维护自己的服务

反康威定律(逆向康威定律):
├─ 如果先设计好微服务架构
├─ 然后按照服务边界组织团队
└─ 可以更好地发挥微服务的优势

实践建议:
✅ 一个服务 = 一个团队(或小组)
✅ 团队规模:3-9人(2个pizza原则)
✅ 团队全栈:前端 + 后端 + 数据库 + 运维
❌ 避免:多个团队维护同一个服务
❌ 避免:一个团队维护多个服务(超过3个)

领域驱动设计(DDD)

DDD核心概念

1. 限界上下文(Bounded Context)
   └─ 一个明确的边界,在边界内部:
       ├─ 术语有唯一的含义
       ├─ 规则是一致的
       └─ 模型是完整的

示例:电商系统的"商品"
├─ 在商品上下文:商品 = 名称 + 价格 + 库存 + 描述
├─ 在订单上下文:商品 = ID + 名称 + 价格(不关心库存)
├─ 在物流上下文:商品 = ID + 重量 + 体积(不关心价格)
└─ 每个上下文对"商品"的理解不同,这是正常的

2. 聚合根(Aggregate Root)
   └─ 一组相关对象的根实体
       ├─ 订单聚合根:Order(包含OrderItem)
       ├─ 用户聚合根:User(包含Address)
       └─ 商品聚合根:Product(包含SKU)

3. 领域事件(Domain Event)
   └─ 领域内发生的重要事件
       ├─ 订单已创建(OrderCreated)
       ├─ 订单已支付(OrderPaid)
       └─ 订单已发货(OrderShipped)

4. 服务拆分原则
   └─ 一个限界上下文 = 一个微服务(或一组微服务)
       ├─ 用户上下文 → 用户服务
       ├─ 商品上下文 → 商品服务
       ├─ 订单上下文 → 订单服务
       └─ 库存上下文 → 库存服务

DDD拆分实践

电商系统领域模型:

核心域(Core Domain):订单域
├─ 订单服务(Order Service)
├─ 支付服务(Payment Service)
└─ 库存服务(Inventory Service)
核心业务逻辑,最复杂,需要最优秀的团队

支撑域(Supporting Domain):用户域、商品域
├─ 用户服务(User Service)
├─ 商品服务(Product Service)
└─ 物流服务(Logistics Service)
支撑核心业务,重要但不复杂

通用域(Generic Domain):营销域、通知域
├─ 营销服务(Marketing Service)
├─ 通知服务(Notification Service)
└─ 搜索服务(Search Service)
通用功能,可以使用第三方服务

拆分依据:
✅ 业务重要性:核心域优先拆分
✅ 变更频率:经常变更的优先拆分
✅ 性能要求:高并发的优先拆分
✅ 团队结构:按团队拆分
❌ 避免:按技术层次拆分(如Controller层服务、Service层服务)
❌ 避免:拆分过细(增加维护成本)

微服务拆分步骤

第一步:识别业务领域
├─ 分析业务流程
├─ 识别核心实体
├─ 定义限界上下文
└─ 绘制上下文地图

第二步:定义服务边界
├─ 一个限界上下文 = 一个微服务
├─ 识别服务依赖关系
├─ 避免循环依赖
└─ 定义服务接口(API)

第三步:拆分数据库
├─ 每个服务独立数据库
├─ 服务间不直接访问数据库
├─ 通过API通信
└─ 处理分布式事务

第四步:迁移策略
├─ 先拆分新功能(增量式)
├─ 再拆分老功能(绞杀者模式)
├─ 逐步迁移流量
└─ 保留回滚能力

第五步:团队组织
├─ 按服务组织团队
├─ 团队全栈(前后端+运维)
├─ 明确团队职责
└─ 建立团队协作机制

微服务的代价与权衡

微服务引入的复杂度

1. 分布式系统复杂度
   ├─ 网络不可靠:调用可能失败、超时
   ├─ 网络延迟:远程调用比本地调用慢100倍
   ├─ 数据一致性:分布式事务难以保证
   └─ 调试困难:跨服务调试、日志分散

2. 运维复杂度
   ├─ 服务数量增加:10个服务 × 3个环境 = 30个实例
   ├─ 部署复杂:需要CI/CD自动化
   ├─ 监控复杂:需要统一监控平台
   └─ 配置管理:配置文件数量激增

3. 开发复杂度
   ├─ 服务间通信:需要定义API、序列化/反序列化
   ├─ 服务依赖:需要管理依赖关系
   ├─ 本地开发:需要启动多个服务
   └─ 集成测试:需要搭建完整环境

4. 组织复杂度
   ├─ 团队协作:需要跨团队沟通
   ├─ 接口变更:需要版本管理
   ├─ 服务治理:需要建立规范
   └─ 技能要求:团队需要全栈能力

何时使用微服务?

✅ 适合使用微服务的场景:

1. 团队规模大(30人+)
   └─ 团队大,需要独立开发、独立部署

2. 业务复杂度高
   └─ 业务模块多,耦合度高,难以维护

3. 并发要求高
   └─ 不同模块的并发差异大,需要独立扩展

4. 技术栈多样
   └─ 不同业务需要不同技术栈

5. 频繁部署
   └─ 需要持续集成、持续部署

❌ 不适合使用微服务的场景:

1. 团队规模小(<10人)
   └─ 团队小,单体应用更高效

2. 业务简单
   └─ 业务逻辑简单,无需拆分

3. 初创项目
   └─ 需求变化快,过早拆分增加成本

4. 并发要求低
   └─ QPS < 1000,单体应用足够

5. 运维能力弱
   └─ 没有DevOps能力,无法管理大量服务

渐进式演进路径

阶段1:单体应用(0-10人)
├─ 快速开发、快速迭代
├─ 验证产品方向
└─ 积累业务复杂度

阶段2:单体应用 + 少量微服务(10-30人)
├─ 拆分高并发模块(如商品服务)
├─ 拆分频繁变更模块(如营销服务)
└─ 保留单体应用的核心逻辑

阶段3:微服务为主(30-100人)
├─ 核心业务全部微服务化
├─ 建立完整的服务治理体系
└─ 团队按服务组织

阶段4:平台化(100人+)
├─ 服务平台化(提供给第三方)
├─ 中台架构(业务中台、数据中台)
└─ 服务网格(Istio)

建议:
✅ 从单体开始,根据需要逐步演进
✅ 不要过早优化,不要盲目追求微服务
✅ 考虑团队能力、业务复杂度、运维成本
❌ 避免为了微服务而微服务

总结:微服务架构的核心价值

微服务解决的核心问题

单体应用的问题 → 微服务的解决方案:

1. 部署困难 → 独立部署
   ├─ 单体:修改任何模块都需要重新部署整个应用
   └─ 微服务:只需部署修改的服务

2. 扩展困难 → 独立扩展
   ├─ 单体:只能整体扩展,资源浪费
   └─ 微服务:按需扩展高并发服务

3. 技术栈单一 → 技术栈自由
   ├─ 单体:整个应用必须用同一技术栈
   └─ 微服务:每个服务可以用不同技术栈

4. 团队协作困难 → 团队独立
   ├─ 单体:所有团队共用一个代码库
   └─ 微服务:每个团队维护自己的服务

5. 故障影响大 → 故障隔离
   ├─ 单体:任何模块崩溃都影响整个应用
   └─ 微服务:故障只影响单个服务

Spring Cloud组件总结

Spring Cloud生态系统:

1. 服务注册与发现
   └─ Nacos、Eureka、Consul

2. 配置管理
   └─ Nacos Config、Apollo、Spring Cloud Config

3. 负载均衡
   └─ Spring Cloud LoadBalancer、Ribbon

4. 服务调用
   └─ OpenFeign、RestTemplate

5. 熔断降级
   └─ Sentinel、Hystrix、Resilience4j

6. API网关
   └─ Spring Cloud Gateway、Zuul

7. 链路追踪
   └─ SkyWalking、Zipkin、Jaeger

8. 服务监控
   └─ Spring Boot Actuator、Prometheus

完整技术栈:
├─ 注册中心:Nacos
├─ 配置中心:Nacos Config
├─ 服务调用:OpenFeign + LoadBalancer
├─ 熔断降级:Sentinel
├─ API网关:Spring Cloud Gateway
├─ 链路追踪:SkyWalking
├─ 服务监控:Prometheus + Grafana
└─ 日志收集:ELK Stack

给从业者的建议

L1(初级):理解微服务基本概念
├─ 掌握服务注册与发现(Nacos)
├─ 掌握配置管理(Nacos Config)
├─ 掌握服务调用(OpenFeign)
└─ 能搭建简单的微服务系统

L2(中级):掌握服务治理
├─ 掌握熔断降级(Sentinel)
├─ 掌握API网关(Gateway)
├─ 掌握链路追踪(SkyWalking)
├─ 掌握分布式事务(Seata)
└─ 能设计复杂的微服务系统

L3(高级):微服务架构设计
├─ 掌握DDD领域驱动设计
├─ 掌握微服务拆分原则
├─ 掌握服务网格(Istio)
├─ 掌握云原生架构
└─ 能主导企业级微服务改造

学习路径:
单体应用 → 简单微服务 → 服务治理 → 架构设计

结语

微服务架构通过"分而治之"的思想,将复杂的单体应用拆分成多个小型、独立的服务,从而解决了单体应用在可扩展性、技术栈选择、团队协作等方面的问题。

核心要点回顾

  1. 服务注册与发现:Nacos统一管理服务地址,支持动态上下线
  2. 配置管理:Nacos Config集中管理配置,支持动态刷新
  3. 负载均衡:多种策略(轮询、随机、加权等),自动故障转移
  4. 熔断降级:Sentinel保护系统,防止雪崩效应
  5. 链路追踪:SkyWalking追踪调用链路,快速定位性能瓶颈

关键收获

  • 从第一性原理理解微服务的设计动机
  • 掌握微服务的五大核心问题及解决方案
  • 理解DDD和康威定律在微服务拆分中的应用
  • 认识到微服务的代价,避免过度设计

下一篇预告第6篇:Spring源码深度解析

  • 如何阅读Spring源码
  • IoC容器启动流程源码剖析
  • AOP代理创建流程源码剖析
  • Spring设计模式精华

系列文章

  1. 为什么我们需要Spring框架?
  2. IoC容器:从手动new到自动装配的演进
  3. AOP:从代码重复到面向切面编程
  4. Spring Boot:约定优于配置的威力
  5. Spring Cloud:从单体到微服务的架构演进(本文)
  6. Spring源码深度解析(待发布)

🤔 思考题

  1. 你的项目适合使用微服务架构吗?为什么?
  2. 如何判断一个单体应用是否应该拆分为微服务?
  3. 微服务的"服务边界"应该如何划分?

欢迎在评论区分享你的思考和经验!

📚 推荐阅读