微服务架构基础
微服务(Microservices)不是"把一个项目拆成很多个 Spring Boot 工程"这么简单。它本质上是一种围绕业务能力组织系统的架构方式,强调:
- 服务边界清晰:每个服务聚焦特定业务领域
- 服务自治:独立开发、测试、部署、扩容
- 去中心化治理:允许不同服务选择不同技术栈
- 配套治理能力同步建设:服务发现、配置、监控、容错等基础设施
微服务的历史演进
从单体到微服务的演变
1. 单体架构时代(2000 年代初)
所有功能打包在一个应用中,部署在单一服务器上。
优点:
- 开发简单,调试方便
- 部署简单,只需部署一个应用
- 事务处理容易(本地事务)
缺点:
- 随着业务增长,代码变得庞大且复杂
- 一个小改动需要重新部署整个应用
- 技术栈被锁定,难以引入新技术
- 扩容只能整体扩容,资源浪费
2. SOA 时代(2005-2010)
面向服务的架构(Service-Oriented Architecture)开始流行。
特点:
- 将应用拆分为粗粒度的服务
- 通过企业服务总线(ESB)连接各个服务
- 强调服务的重用和组合
问题:
- ESB 成为单点故障和性能瓶颈
- 服务边界模糊,粒度不够细
- 仍然存在大量集中式治理
3. 微服务时代(2014 至今)
2014 年,Martin Fowler 和 James Lewis 发表了微服务架构的经典论文,正式提出微服务概念。
核心理念:
微服务架构是一种将单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通过轻量级机制通信(通常是 HTTP API),每个服务围绕业务能力构建,可独立部署、独立扩展,并且可以用不同的编程语言和数据库。
微服务的定义与特征
Martin Fowler 的微服务定义:
- 组件化与解耦:系统由多个独立可替换、可升级的组件组成
- 按业务能力组织:服务围绕业务能力而不是技术层划分
- 产品而非项目:团队对服务的全生命周期负责
- 智能端点与哑管道:服务端点智能,通信管道简单
- 去中心化治理:允许不同服务选择不同技术栈
- 去中心化数据管理:每个服务管理自己的数据库
- 基础设施自动化:高度自动化的部署、测试、监控
- 容错设计:假设服务会失败,设计容错机制
- 演进式设计:逐步演进,而非一次性设计完成
什么是微服务
微服务是将一个大型系统拆分成多个相对独立的小型服务,每个服务围绕一项相对稳定的业务能力构建。
典型电商系统的服务拆分
┌─────────────────────────────────────────────────────┐
│ 电商微服务架构 │
├─────────────────────────────────────────────────────┤
│ 用户服务(User Service) │
│ - 用户注册、登录、认证 │
│ - 用户信息管理 │
│ - 会员等级、积分 │
├─────────────────────────────────────────────────────┤
│ 商品服务(Product Service) │
│ - 商品信息管理 │
│ - 分类、品牌、属性 │
│ - 商品搜索、推荐 │
├─────────────────────────────────────────────────────┤
│ 订单服务(Order Service) │
│ - 订单创建、查询、取消 │
│ - 订单状态流转 │
│ - 订单统计分析 │
├─────────────────────────────────────────────────────┤
│ 库存服务(Inventory Service) │
│ - 库存查询、扣减、回退 │
│ - 库存预警、补货 │
│ - 仓库管理 │
├─────────────────────────────────────────────────────┤
│ 支付服务(Payment Service) │
│ - 支付渠道对接 │
│ - 支付状态管理 │
│ - 退款处理 │
├─────────────────────────────────────────────────────┤
│ 营销服务(Marketing Service) │
│ - 优惠券管理 │
│ - 促销活动 │
│ - 积分兑换 │
└─────────────────────────────────────────────────────┘微服务的核心特征
1. 服务自治
每个服务可以:
- 独立开发:团队独立开发,不受其他服务影响
- 独立测试:完整的测试套件,包括单元测试、集成测试、端到端测试
- 独立部署:部署不影响其他服务
- 独立扩容:根据业务压力独立水平扩展
2. 业务边界清晰
服务划分基于业务能力(Business Capability),而不是技术层:
× 错误拆分(按技术层):
┌─────────────────┐
│ Web 层 │
├─────────────────┤
│ Service 层 │
├─────────────────┤
│ DAO 层 │
├─────────────────┤
│ Database 层 │
└─────────────────┘
√ 正确拆分(按业务能力):
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 用户服务 │ │ 商品服务 │ │ 订单服务 │
└─────────┘ └─────────┘ └─────────┘3. 技术多样性
不同服务可以选择不同的技术栈:
用户服务 → Spring Boot + MySQL
商品服务 → Spring Boot + Elasticsearch
推荐服务 → Python + TensorFlow
统计服务 → Go + ClickHouse
前端服务 → Node.js + React虽然允许技术多样性,但大多数企业仍会限制技术栈,避免:
- 团队学习成本增加
- 运维复杂度提升
- 招聘难度加大
4. 数据独立
每个服务管理自己的数据:
┌──────────────────────┐
│ 用户服务 │
│ ┌────────────────┐ │
│ │ 用户数据库 │ │
│ │ MySQL │ │
│ └────────────────┘ │
└──────────────────────┘
┌──────────────────────┐
│ 商品服务 │
│ ┌────────────────┐ │
│ │ 商品数据库 │ │
│ │ Elasticsearch │ │
│ └────────────────┘ │
└──────────────────────┘
┌──────────────────────┐
│ 订单服务 │
│ ┌────────────────┐ │
│ │ 订单数据库 │ │
│ │ PostgreSQL │ │
│ └────────────────┘ │
└──────────────────────┘关键原则:
- 服务间不共享数据库
- 通过 API 或消息队列获取数据
- 接受最终一致性
为什么会出现微服务
单体架构的痛点
系统在早期通常从单体架构起步,这是非常正常的选择。因为单体在业务早期有明显优势:
- 开发快、调试方便、部署简单、事务处理容易
但随着业务和团队规模增长,单体系统往往会暴露这些问题:
1. 代码复杂度爆炸
单体应用的代码规模:
- 初期:10,000 行代码
- 一年后:100,000 行代码
- 三年后:1,000,000+ 行代码
问题:
- 模块相互影响,牵一发动全身
- 新人上手困难,理解成本高
- 代码质量难以保证2. 部署频率受限
场景:支付团队需要发布一个紧急补丁
单体架构:
- 必须等待其他模块的改动完成
- 回归测试整个系统
- 协调发布窗口
- 风险高,影响范围大
微服务架构:
- 独立测试支付服务
- 独立发布支付服务
- 风险可控,影响范围小3. 扩容效率低
场景:大促期间订单服务压力大
单体架构:
- 只能整体扩容
- 其他模块跟着消耗资源
- 成本高,资源浪费
微服务架构:
- 只扩容订单服务
- 其他服务保持不变
- 成本优化,资源利用率高4. 技术栈锁定
单体架构:
- 所有模块必须使用相同技术栈
- 难以引入新技术(如 AI、大数据)
- 技术债务难以偿还
微服务架构:
- 不同服务可以使用不同技术栈
- 渐进式技术升级
- 技术创新更容易落地5. 团队协作冲突
场景:20 人团队维护一个单体应用
问题:
- 代码冲突频繁
- 测试互相影响
- 发布需要协调
- 责任不清
微服务架构:
- 每个团队负责自己的服务
- 代码隔离,冲突减少
- 独立测试、独立发布
- 责任清晰微服务解决的核心问题
微服务主要解决的是:
-
复杂业务的边界治理问题
- 通过清晰的业务边界,降低系统复杂度
- 领域驱动设计(DDD)提供方法论支持
-
复杂团队的协作问题
- 服务与团队对应,减少协作成本
- 康威定律(Conway's Law)的实践
-
复杂系统的独立发布与独立扩容问题
- 独立部署,降低发布风险
- 按需扩容,优化资源利用
微服务不是什么
下面这些情况不能简单等同于微服务:
× 错误认知
1. 多模块单体应用 ≠ 微服务
// 单体应用的多模块结构
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// 虽然分了多个模块,但仍然是单体
modules/
├── user-module/
├── order-module/
├── product-module/
└── payment-module/
所有模块在同一个进程中运行,共享同一个数据库2. 一个应用部署多个实例 ≠ 微服务
负载均衡
↓
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 实例 1 │ │ 实例 2 │ │ 实例 3 │
│ (完整) │ │ (完整) │ │ (完整) │
└─────────┘ └─────────┘ └─────────┘
这只是水平扩展,不是微服务
每个实例都包含所有功能3. 按数据库表拆项目 ≠ 微服务
× 错误拆分:一表一服务
用户表 → 用户服务
角色表 → 角色服务
权限表 → 权限服务
日志表 → 日志服务
...
问题:
- 业务被切碎
- 调用链路过长
- 性能和稳定性下降4. 服务数量多 ≠ 架构先进
微服务的价值不在于"拆了多少个服务",而在于:
- 边界是否稳定
- 协作是否清晰
- 治理是否可控常见失败案例
很多失败的微服务实践,本质上不是技术组件选错了,而是:
案例 1:边界没拆清
问题:
- 服务 A 调用服务 B
- 服务 B 调用服务 A
- 循环依赖,边界不清
后果:
- 修改一个服务影响其他服务
- 部署顺序复杂
- 排障困难案例 2:服务拆得过细
问题:
- 一个简单的业务拆了 20+ 个服务
- 一个请求经过 10+ 个服务
- 同步调用链路过长
后果:
- 性能下降(网络延迟)
- 稳定性下降(任何一个服务失败都会导致整个请求失败)
- 排障困难(调用链路复杂)案例 3:配套治理没跟上
问题:
- 只拆了服务
- 没有服务发现
- 没有配置中心
- 没有链路追踪
- 没有监控告警
后果:
- 服务治理混乱
- 排障效率低
- 运维成本高微服务解决了什么问题
1. 发布耦合问题
单体系统:
支付模块改动 → 回归测试整个系统 → 协调发布窗口 → 全站发版
↓
影响所有模块微服务:
支付服务改动 → 测试支付服务 → 独立发布支付服务
↓
影响范围可控价值:
- 降低发布风险
- 提高发布频率
- 加快迭代速度
2. 扩容粒度问题
场景:大促期间订单服务压力大
单体架构:
┌─────────────────────────────────────┐
│ 单体应用(4 核 8GB) │
│ ┌───────┬───────┬───────┬───────┐ │
│ │ 用户 │ 商品 │ 订单 │ 支付 │ │
│ │ 模块 │ 模块 │ 模块 │ 模块 │ │
│ └───────┴───────┴───────┴───────┘ │
└─────────────────────────────────────┘
↓ 扩容
┌─────────────────────────────────────┐
│ 单体应用(8 核 16GB) │
│ 所有模块都跟着扩容 │
│ 但只有订单模块压力大 │
│ 其他模块资源浪费 │
└─────────────────────────────────────┘微服务架构:
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ 用户服务 │ │ 商品服务 │ │ 订单服务 │ │ 支付服务 │
│ 2 核 4GB │ │ 2 核 4GB │ │ 2 核 4GB │ │ 2 核 4GB │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
↓ 只扩容订单服务
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ 用户服务 │ │ 商品服务 │ │ 订单服务 │ │ 支付服务 │
│ 2 核 4GB │ │ 2 核 4GB │ │ 8 核 16GB│ │ 2 核 4GB │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
只扩容有压力的服务,资源利用率高价值:
- 精细化资源管理
- 降低成本
- 提高资源利用率
3. 团队协作问题
康威定律(Conway's Law):
设计系统的组织,其产生的设计等同于组织间的沟通结构。
单体架构:
一个 20 人的大团队维护一个单体应用
问题:
- 代码冲突频繁
- 测试互相影响
- 发布需要协调
- 责任不清微服务架构:
用户团队(5 人)→ 用户服务
商品团队(5 人)→ 商品服务
订单团队(5 人)→ 订单服务
支付团队(5 人)→ 支付服务
优势:
- 团队对服务负责
- 接口成为协作契约
- 发布节奏相对独立价值:
- 降低沟通成本
- 提高团队效率
- 责任清晰
4. 技术演进问题
单体架构:
技术栈绑定:
- Java 8 + Spring 4.x
- MySQL 5.6
- Redis 3.x
升级困难:
- 必须整体升级
- 风险高,影响大
- 技术债务积累微服务架构:
技术多样性:
用户服务 → Java 17 + Spring Boot 3.x
商品服务 → Java 17 + Spring Boot 3.x
推荐服务 → Python 3.11 + TensorFlow
统计服务 → Go 1.21 + ClickHouse
实时计算 → Flink 1.17
渐进式升级:
- 新服务使用新技术
- 旧服务逐步迁移
- 技术创新更容易落地价值:
- 技术升级风险可控
- 技术创新容易落地
- 技术债务容易偿还
微服务引入的新复杂度
这是理解微服务最重要的一点:微服务不是简单降低复杂度,而是把复杂度从单体内部转移到分布式协作和治理层面。
1. 网络通信问题
单体架构:
// 进程内调用,稳定可靠
public class OrderService {
@Autowired
private UserService userService;
public void createOrder(Long userId) {
// 直接调用,无网络开销
User user = userService.getUser(userId);
}
}微服务架构:
// 跨进程调用,网络不可靠
public class OrderService {
@Autowired
private UserClient userClient;
public void createOrder(Long userId) {
// 网络调用,存在各种问题
try {
User user = userClient.getUser(userId);
} catch (SocketTimeoutException e) {
// 超时
} catch (ConnectException e) {
// 连接失败
} catch (UnknownHostException e) {
// DNS 解析失败
}
}
}新问题:
- 网络延迟:远程调用比本地调用慢几个数量级
- 网络不可靠:超时、失败、抖动是常态
- 序列化开销:数据需要序列化/反序列化
2. 数据一致性问题
单体架构:
// 本地事务,ACID 保证
@Transactional
public void placeOrder(Long userId, Long productId) {
// 扣减库存
inventoryDao.decrement(productId);
// 创建订单
orderDao.insert(order);
// 扣减余额
accountDao.decrement(userId);
// 一个事务,要么全部成功,要么全部回滚
}微服务架构:
跨服务事务:
订单服务 库存服务 账户服务
│ │ │
│ 创建订单 │ │
├───────────────>│ │
│ │ 扣减库存 │
│ ├───────────────>│
│ │ │ 扣减余额
│ │ │ × 失败
│ │ │
│ 如何回滚? │ │
│ │ │
问题:
- 如何保证跨服务的数据一致性?
- 部分失败时如何处理?
- 如何实现补偿?解决方案:
- 最终一致性
- Saga 模式
- TCC(Try-Confirm-Cancel)
- 本地消息表
3. 服务治理问题
单体架构:
只需要关注:
- 应用本身
- 数据库
- 缓存
- 负载均衡微服务架构:
需要额外关注:
- 服务注册与发现
- 配置中心
- API 网关
- 负载均衡(服务级别)
- 服务熔断与降级
- 服务限流
- 链路追踪
- 日志聚合
- 指标监控
- 告警系统4. 运维复杂度问题
单体架构:
部署:1 个应用
监控:1 个应用
排障:查看单机日志微服务架构:
部署:N 个服务 × M 个实例
监控:N × M 个实例
排障:需要聚合日志、链路追踪5. 测试复杂度问题
单体架构:
测试类型:
- 单元测试
- 集成测试
- 端到端测试
测试环境:
- 本地开发环境
- 测试环境
- 生产环境微服务架构:
测试类型:
- 单元测试
- 集成测试
- 契约测试
- 端到端测试
测试环境:
- 本地开发环境(需要模拟依赖服务)
- 集成测试环境
- 预发布环境
- 生产环境
挑战:
- 依赖服务不可用
- 服务间契约变更
- 测试数据管理微服务架构模式
1. 服务发现模式(Service Discovery)
问题: 服务实例动态变化,如何知道服务的地址?
解决方案:
客户端发现
服务消费者 → 查询注册中心 → 获取服务实例列表 → 负载均衡 → 调用实例示例(Spring Cloud Eureka):
// 服务提供者注册
@SpringBootApplication
@EnableEurekaClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
// 服务消费者调用
@FeignClient(name = "user-service")
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") Long id);
}服务端发现
服务消费者 → 负载均衡器/网关 → 查询注册中心 → 调用实例示例(Kubernetes + Service):
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- port: 8080
targetPort: 80802. API 网关模式(API Gateway)
问题: 客户端如何访问多个服务?
解决方案: 使用 API 网关作为统一入口。
网关职责:
┌─────────────────────────────────────────────────────┐
│ API 网关 │
├─────────────────────────────────────────────────────┤
│ 1. 路由转发 │
│ /api/users/* → 用户服务 │
│ /api/orders/* → 订单服务 │
│ /api/products/*→ 商品服务 │
├─────────────────────────────────────────────────────┤
│ 2. 认证授权 │
│ - JWT 验证 │
│ - 权限检查 │
├─────────────────────────────────────────────────────┤
│ 3. 限流熔断 │
│ - 请求限流 │
│ - 服务熔断 │
├─────────────────────────────────────────────────────┤
│ 4. 日志监控 │
│ - 请求日志 │
│ - 指标统计 │
├─────────────────────────────────────────────────────┤
│ 5. 协议转换 │
│ - HTTP → gRPC │
│ - SOAP → REST │
└─────────────────────────────────────────────────────┘示例(Spring Cloud Gateway):
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("user-service", r -> r.path("/api/users/**")
.filters(f -> f
.stripPrefix(1)
.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter()))
)
.uri("lb://user-service"))
.route("order-service", r -> r.path("/api/orders/**")
.filters(f -> f.stripPrefix(1))
.uri("lb://order-service"))
.build();
}
}3. 断路器模式(Circuit Breaker)
问题: 服务调用失败时,如何防止级联故障?
解决方案: 使用断路器自动熔断。
断路器状态机:
失败率超过阈值
┌──────────────────────┐
│ │
▼ │
┌─────────┐ 调用成功 ┌─────────┐
│ 关闭 │────────────>│ 半开 │
│ (Closed)│ │ (Half-Open)│
└─────────┘ └─────────┘
▲ │
│ 调用失败 │
│ ┌──────────────────┘
│ │
│ ▼
┌─────────┐
│ 打开 │
│ (Open) │
└─────────┘
│
│ 超时后进入半开状态
└────────────┐
│
▼
┌─────────┐
│ 半开 │
└─────────┘示例(Resilience4j):
@Service
public class OrderService {
private final UserClient userClient;
private final CircuitBreaker circuitBreaker;
public OrderService(UserClient userClient) {
this.userClient = userClient;
this.circuitBreaker = CircuitBreaker.ofDefaults("userClient");
}
public User getUserWithFallback(Long userId) {
return circuitBreaker.executeSupplier(() -> userClient.getUser(userId));
}
@CircuitBreaker(name = "userClient", fallbackMethod = "getUserFallback")
public User getUser(Long userId) {
return userClient.getUser(userId);
}
public User getUserFallback(Long userId, Exception e) {
// 降级逻辑
return new User(userId, "默认用户", false);
}
}4. 服务网格模式(Service Mesh)
问题: 每个服务都需要实现服务发现、熔断、限流等能力,代码侵入性强。
解决方案: 使用 Sidecar 代理将基础设施能力下沉。
架构:
┌─────────────────────────────────────────────────┐
│ Pod │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ 服务容器 │ │ Sidecar(Envoy)│ │
│ │ - 业务逻辑 │ │ - 服务发现 │ │
│ │ - 无基础设施代码 │ │ - 负载均衡 │ │
│ │ │ │ - 熔断限流 │ │
│ │ │ │ - 链路追踪 │ │
│ └──────────────────┘ └──────────────────┘ │
│ │ │ │
│ └─────────┬───────────┘ │
│ │ │
└─────────────────────┼──────────────────────────┘
│
通过 Sidecar 通信示例(Istio):
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: user-service
spec:
hosts:
- user-service
http:
- route:
- destination:
host: user-service
subset: v1
weight: 90
- destination:
host: user-service
subset: v2
weight: 10
retries:
attempts: 3
perTryTimeout: 2s5. 事件驱动架构(Event-Driven Architecture)
问题: 服务间同步调用链路过长,稳定性差。
解决方案: 使用消息队列异步解耦。
架构:
同步调用(链路长):
订单服务 → 库存服务 → 支付服务 → 通知服务
↓ ↓ ↓ ↓
200ms 150ms 300ms 100ms
总耗时:750ms
异步调用(解耦):
订单服务 → 消息队列 ← 库存服务
↓
支付服务
↓
通知服务
订单服务只负责发消息,耗时:50ms示例(Spring Cloud Stream + RabbitMQ):
// 订单服务发送消息
@Service
public class OrderService {
@Autowired
private StreamBridge streamBridge;
public void placeOrder(Order order) {
// 保存订单
orderRepository.save(order);
// 发送订单创建事件
streamBridge.send("orderCreated-out-0", new OrderCreatedEvent(order.getId()));
}
}
// 库存服务消费消息
@Service
public class InventoryService {
@Bean
public Consumer<OrderCreatedEvent> orderCreated() {
return event -> {
// 扣减库存
inventoryService.decrement(event.getProductId(), event.getQuantity());
};
}
}6. CQRS 模式(Command Query Responsibility Segregation)
问题: 读写模型混合,复杂度增加。
解决方案: 分离读写模型。
架构:
┌─────────────────────────────────────────────────────┐
│ CQRS 架构 │
├─────────────────────────────────────────────────────┤
│ 命令模型(Command Model) │
│ - 处理写操作 │
│ - 关注数据一致性 │
│ - 使用主数据库(MySQL) │
│ ┌─────────────────────────────────────┐ │
│ │ 写入 → MySQL │ │
│ └─────────────────────────────────────┘ │
│ │ │
│ │ 发布事件 │
│ ▼ │
│ 查询模型(Query Model) │
│ - 处理读操作 │
│ - 关注查询性能 │
│ - 使用读库(Elasticsearch/Redis) │
│ ┌─────────────────────────────────────┐ │
│ │ 读取 ← Elasticsearch/Redis │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘示例:
// 命令模型
@Service
public class OrderCommandService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private ApplicationEventPublisher eventPublisher;
@Transactional
public void createOrder(CreateOrderCommand command) {
Order order = new Order(command);
orderRepository.save(order);
// 发布事件,更新查询模型
eventPublisher.publishEvent(new OrderCreatedEvent(order));
}
}
// 查询模型
@Service
public class OrderQueryService {
@Autowired
private OrderQueryRepository queryRepository;
public OrderSummary getOrderSummary(Long orderId) {
return queryRepository.findById(orderId);
}
@EventListener
public void handle(OrderCreatedEvent event) {
// 更新查询模型
OrderSummary summary = new OrderSummary(event.getOrder());
queryRepository.save(summary);
}
}微服务常见基础设施
基础设施全景图
| 基础设施 | 作用 | 主流组件 | 为什么重要 |
|---|---|---|---|
| API 网关 | 统一入口、鉴权、路由、限流、灰度 | Spring Cloud Gateway、Kong、Nginx | 横切能力集中治理 |
| 注册中心 | 维护服务名到实例地址的映射 | Nacos、Eureka、Consul、Zookeeper | 支撑动态扩缩容和实例变化 |
| 配置中心 | 集中管理配置并支持变更治理 | Nacos、Apollo、Spring Cloud Config | 避免配置散落在本地文件和机器上 |
| 链路追踪 | 还原一次请求的完整调用链 | SkyWalking、Zipkin、Jaeger | 提高分布式排障效率 |
| 日志系统 | 集中收集、查询、分析日志 | ELK(Elasticsearch + Logstash + Kibana) | 分布式环境日志聚合 |
| 指标监控 | 监控运行状态、异常和容量 | Prometheus + Grafana | 没有可观测性就很难稳定运行 |
| 熔断限流 | 保护系统在高压和异常下有序退化 | Sentinel、Resilience4j、Hystrix | 防止局部故障扩散 |
| 消息队列 | 异步解耦、削峰填谷、最终一致性 | Kafka、RocketMQ、RabbitMQ | 缩短主链路,减少同步依赖 |
| 容器编排 | 自动化部署、扩缩容、服务治理 | Kubernetes、Docker Swarm | 微服务部署和运维的基础设施 |
1. 服务注册与发现
Nacos(推荐):
# application.yml
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: dev
group: DEFAULT_GROUP
config:
server-addr: localhost:8848
file-extension: yaml服务注册:
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}2. 配置中心
Nacos Config:
# bootstrap.yml
spring:
cloud:
nacos:
config:
server-addr: localhost:8848
file-extension: yaml
shared-configs:
- data-id: common.yaml
group: DEFAULT_GROUP
refresh: true动态配置刷新:
@RefreshScope
@RestController
public class ConfigController {
@Value("${app.config.value}")
private String configValue;
@GetMapping("/config")
public String getConfig() {
return configValue;
}
}3. API 网关
Spring Cloud Gateway:
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("user-service", r -> r.path("/api/users/**")
.filters(f -> f
.stripPrefix(1)
.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter()))
)
.uri("lb://user-service"))
.build();
}
}4. 链路追踪
SkyWalking:
# 启动参数
java -javaagent:/path/to/skywalking-agent.jar \
-Dskywalking.agent.service_name=user-service \
-Dskywalking.collector.backend_service=localhost:11800 \
-jar user-service.jar查看调用链:
TraceId: 1234567890abcdef
┌─────────────────────────────────────────┐
│ user-service /api/users/123 200ms │
│ ├─ order-service /orders 100ms │
│ └─ payment-service /pay 50ms │
└─────────────────────────────────────────┘5. 监控告警
Prometheus + Grafana:
# prometheus.yml
scrape_configs:
- job_name: 'user-service'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['user-service:8080']Grafana 仪表盘:
- 服务请求量、响应时间、错误率
- JVM 内存、GC、线程
- 数据库连接池、慢查询
- Redis 命中率、延迟
6. 熔断限流
Sentinel:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@RestController
public class OrderController {
@SentinelResource(value = "createOrder", blockHandler = "handleBlock")
@PostMapping("/orders")
public Order createOrder(@RequestBody OrderRequest request) {
// 创建订单逻辑
}
public Order handleBlock(OrderRequest request, BlockException ex) {
// 限流降级逻辑
return new Order("系统繁忙,请稍后重试");
}
}服务拆分的核心原则
1. DDD 领域驱动设计
领域驱动设计(Domain-Driven Design)是微服务拆分的核心方法论。
关键概念:
- 限界上下文(Bounded Context):明确的业务边界
- 聚合(Aggregate):一组相关对象的集合,作为数据修改的单元
- 领域事件(Domain Event):领域内发生的事情
示例:电商系统的领域划分
┌─────────────────────────────────────────────────────┐
│ 用户域(User Context) │
│ 聚合:User、Member、Address │
│ 领域事件:UserRegistered、MemberUpgraded │
├─────────────────────────────────────────────────────┤
│ 商品域(Product Context) │
│ 聚合:Product、Category、Brand │
│ 领域事件:ProductCreated、PriceChanged │
├─────────────────────────────────────────────────────┤
│ 订单域(Order Context) │
│ 聚合:Order、OrderItem │
│ 领域事件:OrderCreated、OrderPaid、OrderShipped │
├─────────────────────────────────────────────────────┤
│ 库存域(Inventory Context) │
│ 聚合:Inventory、Warehouse │
│ 领域事件:StockDeducted、StockRestocked │
└─────────────────────────────────────────────────────┘2. 服务拆分原则
原则 1:按业务能力拆分
正确示例:
√ 按业务能力拆分:
用户服务
- 用户注册、登录
- 用户信息管理
- 会员等级、积分
商品服务
- 商品信息管理
- 分类、品牌、属性
- 商品搜索、推荐
订单服务
- 订单创建、查询、取消
- 订单状态流转错误示例:
× 按技术层拆分:
Web 层服务
- 所有 Controller
Service 层服务
- 所有 Service
DAO 层服务
- 所有 DAO
问题:
- 服务间调用频繁
- 业务逻辑分散
- 边界不清原则 2:高内聚、低耦合
判断标准:
高内聚:
- 服务内部的代码围绕同一业务职责
- 频繁一起变化的功能放在一起
低耦合:
- 服务间通过接口交互
- 服务可以独立部署
- 服务可以独立扩容示例:
// √ 高内聚:订单相关的逻辑都在订单服务
@Service
public class OrderService {
public Order createOrder(CreateOrderCommand command) {
// 校验用户
User user = userClient.getUser(command.getUserId());
// 校验商品
Product product = productClient.getProduct(command.getProductId());
// 创建订单
Order order = new Order(user, product, command.getQuantity());
orderRepository.save(order);
return order;
}
}
// × 低内聚:订单逻辑分散在多个服务
// 用户服务中有创建订单的逻辑
// 商品服务中有计算价格和逻辑
// 订单服务只是保存订单原则 3:数据归属明确
原则:
- 每个服务管理自己的核心数据
- 其他服务通过 API 或消息获取信息
- 避免跨服务直接访问数据库
- 接受最终一致性
示例:
// √ 正确:通过 API 获取用户信息
@FeignClient(name = "user-service")
public interface UserClient {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") Long id);
}
// × 错误:直接连接用户服务的数据库
@Service
public class OrderService {
@Autowired
@Qualifier("userDataSource")
private DataSource userDataSource; // × 不应该这样
}原则 4:先粗后细,避免过度拆分
拆分策略:
第一阶段:粗粒度拆分
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 用户服务 │ │ 商品服务 │ │ 订单服务 │
└─────────┘ └─────────┘ └─────────┘
第二阶段:细粒度拆分(如果需要)
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ 用户服务 │ │ 商品服务 │ │ 订单服务 │ │ 库存服务 │ │ 支付服务 │
└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
第三阶段:进一步拆分(谨慎)
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ 用户服务 │ │ 商品服务 │ │ 订单服务 │ │ 库存服务 │ │ 支付服务 │
└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 搜索服务 │ │ 推荐服务 │ │ 风控服务 │
└─────────┘ └─────────┘ └─────────┘判断是否需要拆分:
| 问题 | 答案 | 是否拆分 |
|---|---|---|
| 是否有独立的业务生命周期? | 是 | √ 拆分 |
| 是否有独立的扩容诉求? | 是 | √ 拆分 |
| 是否由独立的团队负责? | 是 | √ 拆分 |
| 领域边界是否稳定? | 是 | √ 拆分 |
原则 5:同步依赖链路不要过长
问题:
× 同步链路过长:
用户请求
→ API 网关(10ms)
→ 认证服务(50ms)
→ 订单服务(200ms)
→ 用户服务(100ms)
→ 商品服务(100ms)
→ 库存服务(150ms)
→ 支付服务(300ms)
→ 通知服务(100ms)
总耗时:1010ms
任何一个服务失败都会导致整个请求失败优化:
√ 核心流程同步,非核心流程异步:
用户请求
→ API 网关(10ms)
→ 认证服务(50ms)
→ 订单服务(200ms)
→ 库存服务(150ms)【同步】
→ 消息队列(10ms)
→ 支付服务(异步)
→ 通知服务(异步)
总耗时:420ms
核心流程稳定,非核心流程异步化数据一致性解决方案
1. 分布式事务问题
经典场景:下单扣库存
订单服务 库存服务 支付服务
│ │ │
│ 1. 创建订单 │ │
│ √ │ │
├───────────────>│ │
│ │ 2. 扣减库存 │
│ │ √ │
│ ├───────────────>│
│ │ │ 3. 扣款
│ │ │ × 失败
│ │ │
│ 如何回滚? │ 如何回滚? │2. 解决方案对比
| 方案 | 一致性 | 性能 | 复杂度 | 适用场景 |
|---|---|---|---|---|
| 2PC(两阶段提交) | 强一致性 | 低 | 高 | 传统数据库 |
| TCC | 最终一致性 | 中 | 高 | 金融场景 |
| Saga | 最终一致性 | 高 | 中 | 长事务 |
| 本地消息表 | 最终一致性 | 高 | 低 | 简单场景 |
| 事务消息 | 最终一致性 | 高 | 低 | 消息队列场景 |
3. Saga 模式
编排式 Saga:
@Service
public class OrderSagaOrchestrator {
@Autowired
private OrderService orderService;
@Autowired
private InventoryService inventoryService;
@Autowired
private PaymentService paymentService;
public void placeOrder(PlaceOrderCommand command) {
// 步骤 1:创建订单
Order order = orderService.createOrder(command);
try {
// 步骤 2:扣减库存
inventoryService.decrement(command.getProductId(), command.getQuantity());
try {
// 步骤 3:支付
paymentService.pay(order.getId(), command.getAmount());
// 步骤 4:确认订单
orderService.confirmOrder(order.getId());
} catch (PaymentException e) {
// 补偿:恢复库存
inventoryService.increment(command.getProductId(), command.getQuantity());
// 补偿:取消订单
orderService.cancelOrder(order.getId());
throw e;
}
} catch (InventoryException e) {
// 补偿:取消订单
orderService.cancelOrder(order.getId());
throw e;
}
}
}协同式 Saga(事件驱动):
// 订单服务
@Service
public class OrderService {
@Transactional
public Order createOrder(PlaceOrderCommand command) {
Order order = new Order(command);
orderRepository.save(order);
// 发布事件
eventPublisher.publish(new OrderCreatedEvent(order));
return order;
}
@Transactional
@EventListener
public void handle(PaymentFailedEvent event) {
// 补偿:取消订单
orderRepository.findById(event.getOrderId())
.ifPresent(order -> order.cancel());
}
}
// 库存服务
@Service
public class InventoryService {
@Transactional
@EventListener
public void handle(OrderCreatedEvent event) {
try {
inventoryRepository.decrement(event.getProductId(), event.getQuantity());
eventPublisher.publish(new InventoryDeductedEvent(event.getOrderId()));
} catch (Exception e) {
eventPublisher.publish(new InventoryDeductionFailedEvent(event.getOrderId()));
}
}
@Transactional
@EventListener
public void handle(PaymentFailedEvent event) {
// 补偿:恢复库存
inventoryRepository.increment(event.getProductId(), event.getQuantity());
}
}4. 本地消息表
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private MessageRepository messageRepository;
@Transactional
public void placeOrder(PlaceOrderCommand command) {
// 1. 创建订单
Order order = new Order(command);
orderRepository.save(order);
// 2. 保存消息到本地消息表(同一个事务)
Message message = new Message(
"order-created",
JSON.toJSONString(new OrderCreatedEvent(order))
);
messageRepository.save(message);
}
}
// 定时任务扫描并发送消息
@Scheduled(fixedDelay = 5000)
public void sendPendingMessages() {
List<Message> messages = messageRepository.findPendingMessages();
for (Message message : messages) {
try {
mqProducer.send(message.getTopic(), message.getContent());
message.markAsSent();
messageRepository.save(message);
} catch (Exception e) {
// 下次重试
}
}
}容器化与 Kubernetes
1. Docker 容器化
Dockerfile:
# 多阶段构建
FROM eclipse-temurin:17-jdk-alpine AS build
WORKDIR /app
COPY . .
RUN ./gradlew build -x test
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=build /app/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]构建与运行:
# 构建镜像
docker build -t user-service:1.0.0 .
# 运行容器
docker run -d \
--name user-service \
-p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/user_db \
user-service:1.0.02. Kubernetes 部署
Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
labels:
app: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: user-service:1.0.0
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: SPRING_DATASOURCE_URL
valueFrom:
configMapKeyRef:
name: user-service-config
key: database.url
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5Service:
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- port: 80
targetPort: 8080
type: ClusterIPIngress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: user-service-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: api.example.com
http:
paths:
- path: /users
pathType: Prefix
backend:
service:
name: user-service
port:
number: 803. Kubernetes 微服务治理
配置管理:
# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: user-service-config
data:
application.yaml: |
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://mysql:3306/user_db
username: root
password: password
---
# Secret
apiVersion: v1
kind: Secret
metadata:
name: user-service-secret
type: Opaque
data:
database.password: cGFzc3dvcmQ= # base64 编码自动扩缩容:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: user-service-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: user-service
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80可观测性建设
1. 三大支柱
┌─────────────────────────────────────────────────────┐
│ 可观测性(Observability) │
├─────────────────────────────────────────────────────┤
│ 指标(Metrics) │
│ - 请求量、错误率、响应时间 │
│ - CPU、内存、磁盘、网络 │
│ - 业务指标(订单量、支付金额) │
│ 工具:Prometheus + Grafana │
├─────────────────────────────────────────────────────┤
│ 日志(Logs) │
│ - 应用日志 │
│ - 访问日志 │
│ - 错误日志 │
│ 工具:ELK(Elasticsearch + Logstash + Kibana) │
├─────────────────────────────────────────────────────┤
│ 链路追踪(Traces) │
│ - 调用链路 │
│ - 性能瓶颈 │
│ - 错误定位 │
│ 工具:SkyWalking、Zipkin、Jaeger │
└─────────────────────────────────────────────────────┘2. 指标监控
Spring Boot Actuator:
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
tags:
application: ${spring.application.name}
export:
prometheus:
enabled: truePrometheus 配置:
scrape_configs:
- job_name: 'spring-boot'
metrics_path: '/actuator/prometheus'
static_configs:
- targets:
- 'user-service:8080'
- 'order-service:8080'
- 'product-service:8080'Grafana 仪表盘:
- JVM 监控(堆内存、GC、线程)
- HTTP 监控(请求量、响应时间、错误率)
- 数据库监控(连接池、慢查询)
- 自定义业务指标
3. 日志聚合
日志规范:
@Slf4j
@RestController
public class OrderController {
@PostMapping("/orders")
public Order createOrder(@RequestBody OrderRequest request) {
log.info("[创建订单] 开始处理, userId={}, productId={}",
request.getUserId(), request.getProductId());
try {
Order order = orderService.createOrder(request);
log.info("[创建订单] 处理成功, orderId={}", order.getId());
return order;
} catch (Exception e) {
log.error("[创建订单] 处理失败, userId={}, error={}",
request.getUserId(), e.getMessage(), e);
throw e;
}
}
}Logback 配置:
<configuration>
<springProperty scope="context" name="appName" source="spring.application.name"/>
<appender name="JSON" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.log</file>
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"app_name":"${appName}"}</customFields>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/application.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="JSON"/>
</root>
</configuration>ELK 架构:
应用服务 → Filebeat → Logstash → Elasticsearch → Kibana4. 链路追踪
Spring Cloud Sleuth + Zipkin:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>spring:
zipkin:
base-url: http://zipkin:9411
sleuth:
sampler:
probability: 1.0 # 采样率 100%SkyWalking:
java -javaagent:/path/to/skywalking-agent.jar \
-Dskywalking.agent.service_name=user-service \
-Dskywalking.collector.backend_service=localhost:11800 \
-jar user-service.jar链路追踪示例:
TraceId: 1234567890abcdef
SpanId: a1b2c3d4
┌─────────────────────────────────────────────┐
│ user-service │
│ Span: GET /api/users/123 │
│ Duration: 200ms │
│ └─ order-service │
│ Span: POST /orders │
│ Duration: 150ms │
│ └─ inventory-service │
│ Span: POST /inventory/decrement │
│ Duration: 80ms │
└─────────────────────────────────────────────┘主流微服务框架对比
Spring Cloud Alibaba vs Spring Cloud Netflix vs Dubbo
| 特性 | Spring Cloud Alibaba | Spring Cloud Netflix | Dubbo |
|---|---|---|---|
| 注册中心 | Nacos | Eureka(已停止维护) | Nacos、Zookeeper |
| 配置中心 | Nacos | Spring Cloud Config | 不提供 |
| 负载均衡 | Ribbon(已废弃)→ Spring Cloud LoadBalancer | Ribbon | Dubbo 自带 |
| 服务调用 | OpenFeign | OpenFeign | RPC(Dubbo 协议) |
| 熔断限流 | Sentinel | Hystrix(已停止维护) | Sentinel |
| 网关 | Spring Cloud Gateway | Zuul(已停止维护) | 不提供 |
| 社区活跃度 | 高 | 低(Netflix 组件已停止维护) | 高 |
| 学习曲线 | 中等 | 中等 | 较陡 |
| 适用场景 | Spring Boot 项目 | 传统 Spring Cloud 项目 | RPC 调用、高性能场景 |
推荐方案
新项目推荐:
Spring Cloud Alibaba(2022.x 及以上版本):
- 注册中心:Nacos
- 配置中心:Nacos
- 服务调用:OpenFeign
- 熔断限流:Sentinel
- 网关:Spring Cloud Gateway
- 链路追踪:SkyWalking
- 监控:Prometheus + GrafanaRPC 场景推荐:
Dubbo 3.x:
- 注册中心:Nacos
- 配置中心:Nacos
- 服务调用:Dubbo RPC
- 熔断限流:Sentinel
- 网关:Higress 或 Spring Cloud Gateway实战案例:电商微服务架构
系统架构图
┌─────────────────────────────────────────────────────────────┐
│ 客户端层 │
│ Web App │ Mobile App │ 小程序 │ 第三方系统 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 负载均衡层 │
│ Nginx / SLB │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ API 网关层 │
│ Spring Cloud Gateway / Kong │
│ - 路由转发 │
│ - 认证授权 │
│ - 限流熔断 │
│ - 日志监控 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 业务服务层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │用户服务 │ │商品服务 │ │订单服务 │ │支付服务 │ │
│ │ │ │ │ │ │ │ │ │
│ │- 注册登录 │ │- 商品管理 │ │- 订单创建 │ │- 支付处理 │ │
│ │- 用户信息 │ │- 库存管理 │ │- 订单查询 │ │- 退款处理 │ │
│ │- 会员积分 │ │- 价格策略 │ │- 订单状态 │ │- 支付渠道 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │营销服务 │ │搜索服务 │ │推荐服务 │ │通知服务 │ │
│ │ │ │ │ │ │ │ │ │
│ │- 优惠券 │ │- 商品搜索 │ │- 个性化推荐│ │- 短信通知 │ │
│ │- 促销活动 │ │- 全文检索 │ │- 协同过滤 │ │- 邮件通知 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 基础设施层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │注册中心 │ │配置中心 │ │消息队列 │ │链路追踪 │ │
│ │ Nacos │ │ Nacos │ │ RocketMQ │ │SkyWalking│ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │监控告警 │ │日志系统 │ │缓存服务 │ │搜索服务 │ │
│ │Prometheus│ │ ELK │ │ Redis │ │ ES │ │
│ │ Grafana │ │ │ │ │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 数据层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ MySQL │ │ Redis │ │Elasticsearch│ │ MongoDB │ │
│ │ 主从复制 │ │ 集群 │ │ 集群 │ │ 副本集 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘示例代码:订单服务
订单创建流程:
@Service
@Slf4j
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private UserClient userClient;
@Autowired
private ProductClient productClient;
@Autowired
private InventoryClient inventoryClient;
@Autowired
private ApplicationEventPublisher eventPublisher;
@Transactional
@GlobalTransactional // Seata 分布式事务
public Order createOrder(CreateOrderCommand command) {
// 1. 校验用户
User user = userClient.getUser(command.getUserId());
if (user == null || !user.isEnabled()) {
throw new BusinessException("用户状态异常");
}
// 2. 校验商品
Product product = productClient.getProduct(command.getProductId());
if (product == null || !product.isOnSale()) {
throw new BusinessException("商品不可购买");
}
// 3. 扣减库存
boolean success = inventoryClient.decrement(
command.getProductId(),
command.getQuantity()
);
if (!success) {
throw new BusinessException("库存不足");
}
// 4. 创建订单
Order order = new Order(
user.getId(),
product.getId(),
command.getQuantity(),
product.getPrice().multiply(new BigDecimal(command.getQuantity()))
);
orderRepository.save(order);
// 5. 发布订单创建事件
eventPublisher.publishEvent(new OrderCreatedEvent(order));
log.info("[创建订单] 成功, orderId={}, userId={}, productId={}",
order.getId(), user.getId(), product.getId());
return order;
}
}Feign 客户端:
@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient {
@GetMapping("/api/users/{id}")
User getUser(@PathVariable("id") Long id);
}
@Component
public class UserClientFallback implements UserClient {
@Override
public User getUser(Long id) {
// 降级逻辑
return new User(id, "默认用户", false);
}
}微服务最佳实践
1. 服务拆分最佳实践
何时拆分:
√ 应该拆分的情况:
- 业务边界清晰且稳定
- 有独立的扩容诉求
- 团队规模足够(每个服务至少 3-5 人)
- 具备完善的基础设施(服务发现、配置、监控)
× 不应该拆分的情况:
- 业务仍在快速试错
- 团队规模小(< 10 人)
- 基础设施不完善
- 领域边界不清晰拆分步骤:
1. 领域建模(DDD)
- 识别限界上下文
- 定义聚合和领域事件
2. 服务划分
- 按业务能力拆分
- 先粗后细
3. 数据拆分
- 数据归属明确
- 避免共享数据库
4. 接口定义
- 契约优先
- 版本管理
5. 基础设施建设
- 服务发现
- 配置中心
- 链路追踪
- 监控告警2. 服务治理最佳实践
服务发现:
- 使用 Nacos 作为注册中心和配置中心
- 配置健康检查
- 合理设置心跳时间
- 多注册中心支持(异地多活)配置管理:
- 配置分层:全局配置 → 应用配置 → 环境配置
- 敏感配置使用加密
- 配置变更审计
- 灰度发布支持容错设计:
- 设置合理的超时时间
- 配置重试策略(幂等接口)
- 使用断路器防止级联故障
- 设计降级策略
- 限流保护系统3. 数据管理最佳实践
数据库选型:
用户服务 → MySQL(事务性要求高)
商品服务 → Elasticsearch(搜索需求)
推荐服务 → Redis + MongoDB(推荐算法)
订单服务 → MySQL(事务性要求高)
支付服务 → MySQL(事务性要求高)
日志服务 → Elasticsearch(日志检索)数据一致性:
- 核心流程:Saga 模式
- 简单场景:本地消息表
- 高可靠性:TCC
- 接受最终一致性4. 运维最佳实践
CI/CD:
# GitLab CI/CD
stages:
- build
- test
- docker
- deploy
build:
stage: build
script:
- ./gradlew build -x test
test:
stage: test
script:
- ./gradlew test
docker:
stage: docker
script:
- docker build -t registry.example.com/user-service:$CI_COMMIT_SHA .
- docker push registry.example.com/user-service:$CI_COMMIT_SHA
deploy:
stage: deploy
script:
- kubectl set image deployment/user-service user-service=registry.example.com/user-service:$CI_COMMIT_SHA
only:
- master灰度发布:
# Istio VirtualService
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: user-service
spec:
hosts:
- user-service
http:
- match:
- headers:
canary:
exact: "true"
route:
- destination:
host: user-service
subset: v2
- route:
- destination:
host: user-service
subset: v1
weight: 95
- destination:
host: user-service
subset: v2
weight: 5常见误区
误区一:团队规模和业务复杂度还没到位,就把微服务当成默认高级架构
真相:
微服务不是默认最优解,很多时候模块化单体才是更理性的阶段性选择。
何时选择微服务:
√ 应该选择微服务:
- 业务复杂度高
- 团队规模大(> 20 人)
- 边界稳定
- 基础设施完善
× 不应该选择微服务:
- 业务简单
- 团队规模小(< 10 人)
- 业务快速试错期
- 基础设施不完善误区二:按数据库表拆服务
真相:
按表拆服务很容易形成"一表一服务",最后业务被切碎,调用链路变得又长又脆。
正确做法:
√ 按业务能力拆分:
订单服务
- 订单表
- 订单项表
- 订单状态流转表
库存服务
- 库存表
- 仓库表
- 库存流水表
× 按表拆分:
订单表服务
订单项服务
库存表服务
仓库表服务
...误区三:服务拆开了,但还共享一个数据库
真相:
共享数据库会直接破坏服务自治,让边界失效。
问题:
用户服务 ─┐
商品服务 ─┼→ 共享数据库
订单服务 ─┤
支付服务 ─┘
问题:
- 数据修改互相影响
- 发布耦合
- 排障困难
- 边界形同虚设正确做法:
用户服务 → 用户数据库
商品服务 → 商品数据库
订单服务 → 订单数据库
支付服务 → 支付数据库
通过 API 或消息获取数据误区四:核心业务流程全部同步串联
真相:
同步链路过长会显著降低系统稳定性,非核心流程应优先考虑异步化。
问题:
× 全同步:
用户请求
→ 订单服务(200ms)
→ 库存服务(150ms)
→ 支付服务(300ms)
→ 积分服务(100ms)
→ 通知服务(100ms)
总耗时:850ms
任何一个服务失败都会导致整个请求失败优化:
√ 核心同步,非核心异步:
用户请求
→ 订单服务(200ms)
→ 库存服务(150ms)
→ 支付服务(300ms)
→ 消息队列(10ms)
→ 积分服务(异步)
→ 通知服务(异步)
总耗时:660ms
非核心流程异步化,提高稳定性误区五:只关注技术拆分,不关注治理和协作
真相:
微服务能不能长期跑稳,往往不取决于拆分动作本身,而取决于治理体系是否成熟。
需要关注:
√ 技术层面:
- 服务发现
- 配置中心
- 熔断限流
- 链路追踪
- 监控告警
√ 协作层面:
- 接口契约
- 文档管理
- 变更流程
- 责任边界
√ 组织层面:
- 团队与服务的对应关系
- 发布节奏
- 值班机制误区六:过度追求技术多样性
真相:
技术多样性是一把双刃剑,过多的技术栈会增加学习和运维成本。
建议:
√ 主流技术栈统一:
- 编程语言:Java(大部分服务)
- 框架:Spring Boot / Spring Cloud
- 数据库:MySQL(事务服务)、Redis(缓存)、Elasticsearch(搜索)
√ 特殊场景允许差异:
- 推荐服务:Python(机器学习)
- 实时计算:Flink
- 前端渲染:Node.js微服务与模块化单体的关系
很多团队真正需要的第一步,并不是微服务,而是模块化单体。
模块化单体的特征
┌─────────────────────────────────────────────┐
│ 模块化单体应用 │
│ ┌─────────────────────────────────────┐ │
│ │ 用户模块 │ │
│ │ - UserService │ │
│ │ - UserRepository │ │
│ │ - UserController │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ 订单模块 │ │
│ │ - OrderService │ │
│ │ - OrderRepository │ │
│ │ - OrderController │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ 商品模块 │ │
│ │ - ProductService │ │
│ │ - ProductRepository │ │
│ │ - ProductController │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
特点:
- 单一进程
- 模块边界清晰
- 部署简单
- 事务处理容易对比分析
| 对比项 | 模块化单体 | 微服务 |
|---|---|---|
| 部署方式 | 整体部署 | 独立部署 |
| 调用方式 | 进程内调用 | 网络调用 |
| 事务处理 | 本地事务,简单 | 分布式事务,复杂 |
| 运维复杂度 | 低 | 高 |
| 团队要求 | 小团队即可 | 需要成熟的团队和基础设施 |
| 技术栈 | 统一 | 可以多样化 |
| 扩容方式 | 整体扩容 | 按需扩容 |
| 故障隔离 | 弱 | 强 |
| 学习成本 | 低 | 高 |
选择建议
优先选择模块化单体:
- 团队规模较小(< 20 人)
- 业务仍在快速试错
- 领域边界尚未稳定
- 运维与治理能力还不成熟
- 项目初期考虑微服务:
- 业务复杂度高
- 团队规模大(> 20 人)
- 业务边界稳定
- 有独立的扩容诉求
- 基础设施完善演进路径
第一阶段:单体应用
↓ 业务增长,代码复杂度提升
第二阶段:模块化单体
↓ 业务复杂度继续增长,团队扩大
第三阶段:微服务
↓ 按需拆分,逐步演进
第四阶段:服务网格 / 云原生面试要点
基础概念题
Q1:什么是微服务架构?它的核心特征是什么?
参考答案:
微服务架构是一种将单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通过轻量级机制通信。
核心特征:
- 服务自治:独立开发、测试、部署、扩容
- 业务边界清晰:按业务能力组织,而非技术层
- 技术多样性:允许不同服务使用不同技术栈
- 数据独立:每个服务管理自己的数据库
- 基础设施自动化:高度自动化的部署、测试、监控
- 容错设计:假设服务会失败,设计容错机制
- 演进式设计:逐步演进,而非一次性设计
Q2:微服务解决了什么问题?又引入了什么新问题?
参考答案:
解决的问题:
- 发布耦合:独立部署,降低发布风险
- 扩容粒度:按需扩容,提高资源利用率
- 团队协作:服务与团队对应,减少协作成本
- 技术演进:允许渐进式技术升级
引入的新问题:
- 网络通信:网络延迟、超时、失败
- 数据一致性:分布式事务复杂
- 服务治理:需要注册、配置、监控等基础设施
- 运维复杂度:服务数量多,运维难度大
- 测试复杂度:需要契约测试、端到端测试
Q3:微服务拆分的核心原则是什么?
参考答案:
- 按业务能力拆分:基于领域边界,而非技术层或数据库表
- 高内聚、低耦合:服务内部高内聚,服务间低耦合
- 数据归属明确:每个服务管理自己的数据,避免共享数据库
- 先粗后细:从粗粒度开始,逐步细化
- 链路长度适中:同步链路不要过长,非核心流程异步化
Q4:微服务和 SOA 的区别?
参考答案:
| 特性 | SOA | 微服务 |
|---|---|---|
| 服务粒度 | 粗粒度 | 细粒度 |
| 通信方式 | ESB(企业服务总线) | 轻量级协议(HTTP/REST) |
| 治理方式 | 集中式治理 | 去中心化治理 |
| 数据管理 | 共享数据库 | 每个服务独立数据库 |
| 技术栈 | 统一 | 多样化 |
| 部署方式 | 依赖 ESB | 独立部署 |
Q5:如何保证微服务的数据一致性?
参考答案:
微服务架构中,数据一致性主要通过最终一致性实现:
常见方案:
-
Saga 模式
- 将分布式事务拆分为多个本地事务
- 每个步骤有对应的补偿操作
- 适用于长事务
-
TCC(Try-Confirm-Cancel)
- Try:预留资源
- Confirm:确认提交
- Cancel:取消预留
- 适用于金融场景
-
本地消息表
- 业务操作和消息写入在同一个事务
- 定时任务扫描并发送消息
- 消费者保证幂等性
-
事务消息
- RocketMQ 提供事务消息机制
- 半消息 + 提交/回滚
核心原则:
- 核心流程同步保证必要校验
- 非核心流程异步化
- 接受最终一致性
- 设计幂等性和补偿机制
实战理解题
Q1:如何判断是否应该使用微服务架构?
参考答案:
应该考虑的因素:
-
业务复杂度
- 高复杂度:考虑微服务
- 低复杂度:单体或模块化单体
-
团队规模
- 大团队(> 20 人):微服务
- 小团队(< 10 人):单体
-
业务稳定性
- 稳定:微服务
- 快速试错:单体
-
扩容需求
- 独立扩容需求明显:微服务
- 整体扩容即可:单体
-
基础设施
- 完善的 CI/CD、监控、日志:微服务
- 基础设施不完善:单体
推荐路径:
单体应用 → 模块化单体 → 微服务 → 云原生不要为了"先进"而选择微服务,要根据实际情况选择合适的架构。
Q2:微服务架构中,如何设计 API 网关?
参考答案:
网关职责:
- 路由转发:将请求路由到对应的服务
- 认证授权:统一的身份认证和权限校验
- 限流熔断:保护后端服务
- 协议转换:HTTP → gRPC、SOAP → REST
- 日志监控:请求日志、指标统计
- 灰度发布:流量控制和分流
技术选型:
- Spring Cloud Gateway:Spring Cloud 生态,功能丰富
- Kong:基于 Nginx,高性能
- APISIX:Apache 项目,云原生
设计要点:
- 无状态:网关不存储业务状态
- 高可用:多实例部署,负载均衡
- 性能优化:缓存、异步非阻塞
- 安全加固:防止 SQL 注入、XSS、CSRF
Q3:如何进行微服务的性能优化?
参考答案:
优化维度:
-
网络优化
- 减少同步调用链路长度
- 使用 gRPC 替代 HTTP(高性能场景)
- 启用 HTTP/2
- 使用连接池
-
缓存优化
- 多级缓存(本地缓存 + 分布式缓存)
- 热点数据预热
- 缓存穿透、击穿、雪崩防护
-
数据库优化
- 读写分离
- 分库分表
- 索引优化
- 慢查询治理
-
异步化
- 非核心流程异步化
- 使用消息队列解耦
- 削峰填谷
-
服务治理
- 熔断降级
- 限流保护
- 服务隔离
Q4:如何设计微服务的灰度发布方案?
参考答案:
灰度发布策略:
-
基于权重
yaml# Istio VirtualService http: - route: - destination: host: user-service subset: v1 weight: 90 - destination: host: user-service subset: v2 weight: 10 -
基于请求头
yamlhttp: - match: - headers: canary: exact: "true" route: - destination: host: user-service subset: v2 - route: - destination: host: user-service subset: v1 -
基于 Cookie
发布流程:
1. 部署新版本(v2)
2. 内部测试(100% 流量到 v2,仅限测试用户)
3. 小流量灰度(5% → 10% → 30% → 50%)
4. 全量发布(100%)
5. 下线旧版本(v1)监控指标:
- 错误率
- 响应时间
- 业务指标(转化率、订单量)
回滚策略:
- 快速回滚(调整流量权重)
- 保留旧版本足够时间(至少 24 小时)
下一步学习
继续深入微服务相关主题:
本节小结:
- 微服务是一种围绕业务能力组织系统的架构方式,强调服务自治、边界清晰、独立部署
- 微服务解决了发布耦合、扩容粒度、团队协作、技术演进等问题
- 微服务引入了网络通信、数据一致性、服务治理、运维复杂度等新问题
- 服务拆分应基于业务能力(DDD),遵循高内聚低耦合、数据独立、先粗后细的原则
- 微服务需要完善的基础设施:服务发现、配置中心、网关、链路追踪、监控告警
- 数据一致性通过最终一致性实现,常见方案有 Saga、TCC、本地消息表
- 微服务不是默认最优解,模块化单体往往是更理性的阶段性选择
- 成功的微服务需要技术、协作、组织三方面的配套建设
版本差异(Spring Cloud 旧版 → 2025.x)
| 特性 | 旧版(本文编写时) | 当前(Spring Cloud 2025.x / Boot 3.5.x) |
|---|---|---|
| 架构组件 | Eureka/Ribbon/Hystrix/Zuul(已停止维护) | LoadBalancer/Gateway/Resilience4j/Nacos |
| 版本对应 | Hoxton/2020.x + Boot 2.x + JDK 8 | 2025.x + Boot 3.5.x + JDK 17+ |
| 云原生 | 初步支持 | 全面云原生(K8s、虚拟线程、GraalVM 支持) |
| 链路追踪 | Sleuth + Zipkin | Micrometer Tracing + Zipkin/OTel |
本文为通用微服务概念讲解,架构思想长期有效;落地时请采用 Spring Cloud 2025.x + Spring Boot 3.5.x 技术栈(JDK 17+)。