微服务架构落地,Spring Cloud 全家桶组件总览
把一个巨型单体应用拆成几十个微服务,听起来很酷。但真正落地时,你会发现问题才刚刚开始:服务之间怎么通信?配置怎么管理?服务挂了怎么办?
这就是 Spring Cloud 全家桶要解决的问题。
从一个问题开始
假设你负责重构一个电商系统,从单体拆成微服务架构:
┌─────────────────────────────────────────────────────────┐
│ 单体应用 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 用户模块 │ │ 订单模块 │ │ 商品模块 │ │ 支付模块 │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────────────────┘
↓ 拆分
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 用户服务 │ │ 订单服务 │ │ 商品服务 │ │ 支付服务 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘拆分后,你面临的问题:
- 服务发现:订单服务怎么知道用户服务的地址?
- 负载均衡:多个用户服务实例,订单服务该调用哪个?
- 配置管理:几十个服务,每个都要独立配置,怎么管?
- 流量控制:双十一流量暴涨,怎么限流?
- 链路追踪:请求经过 5 个服务,出问题了怎么定位?
- 服务通信:服务间调用,用 HTTP 还是 RPC?
Spring Cloud 全家桶,就是这些问题的标准答案。
组件全景图
┌─────────────────┐
│ API 网关 │
│ (Gateway) │
└────────┬────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 用户服务 │◄──────►│ 订单服务 │◄──────►│ 商品服务 │
│ :8081 │ │ :8082 │ │ :8083 │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
└──────────────────────┼──────────────────────┘
│
┌───────▼───────┐
│ 注册中心 │
│ (Nacos) │
└───────┬───────┘
│
┌───────▼───────┐
│ 配置中心 │
│ (Nacos) │
└───────┬───────┘
│
┌───────▼───────┐
│ 限流熔断 │
│ (Sentinel) │
└───────┬───────┘
│
┌───────▼───────┐
│ 链路追踪 │
│ (Zipkin) │
└───────────────┘核心组件详解
一、服务注册与发现
解决问题:服务如何知道其他服务的地址?
Nacos(推荐)
阿里的开源产品,同时支持注册中心和配置中心。
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: dev
group: DEFAULT_GROUP
config:
server-addr: 127.0.0.1:8848
file-extension: yaml@SpringBootApplication
@EnableDiscoveryClient // 开启服务注册
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}核心特性:
- 支持临时实例和持久实例
- 支持命名空间隔离
- 提供控制台管理界面
- 集成配置中心,一站式解决方案
二、声明式 HTTP 客户端
解决问题:服务间 HTTP 调用如何更简单?
OpenFeign
用注解的方式调用 HTTP 接口,像调用本地方法一样简单。
@FeignClient(name = "user-service", path = "/users")
public interface UserClient {
@GetMapping("/{id}")
User getUser(@PathVariable Long id);
@GetMapping("/list")
List<User> getUsers(@RequestParam List<Long> ids);
}@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private UserClient userClient;
@GetMapping("/{orderId}")
public Order getOrder(@PathVariable Long orderId) {
// 调用用户服务获取用户信息
User user = userClient.getUser(order.getUserId());
return order;
}
}核心特性:
- 基于注解声明式定义 RESTful 接口
- 集成 Ribbon,自动负载均衡
- 支持 Hystrix 熔断(可切换为 Sentinel)
- 支持请求/响应压缩
- 支持日志打印
三、统一网关
解决问题:外部请求如何统一入口?
Spring Cloud Gateway
异步非阻塞的网关,性能比 Zuul 更优。
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 100
redis-rate-limiter.burstCapacity: 200核心特性:
- 异步非阻塞模型,性能优秀
- 内置多种路由断言(Path、Header、Query 等)
- 支持自定义过滤器
- 集成限流、熔断
- 支持动态路由
四、限流与熔断
解决问题:服务挂了怎么办?怎么防止雪崩?
Sentinel
阿里的流量防卫兵,专注于流量控制和熔断降级。
@RestController
@RequestMapping("/order")
public class OrderController {
@GetMapping("/create")
@SentinelResource(value = "createOrder",
blockHandler = "createOrderBlockHandler",
fallback = "createOrderFallback")
public Result<Order> createOrder(@RequestParam Long userId) {
// 业务逻辑
return Result.ok(orderService.create(userId));
}
// 限流处理
public Result<Order> createOrderBlockHandler(Long userId, BlockException e) {
return Result.fail("访问太频繁,请稍后再试");
}
// 降级处理
public Result<Order> createOrderFallback(Long userId, Throwable e) {
return Result.fail("服务繁忙,请稍后再试");
}
}核心特性:
- 流量控制(QPS、并发数)
- 熔断降级(RT、异常比例、异常数)
- 系统自适应保护
- 热点参数限流
- 规则持久化
五、配置中心
解决问题:配置如何集中管理?如何实现热更新?
Nacos Config
配置管理的五把钥匙:命名空间(Namespace)、分组(Group)、Data ID、配置内容、配置版本。
# bootstrap.yml
spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
namespace: dev
group: ORDER_GROUP
file-extension: yaml
refresh-enabled: true
profiles:
active: prodData ID 完整命名规则:
${spring.application.name}-${spring.profiles.active}.${file-extension}例如:
order-service-prod.yaml
核心特性:
- 配置热更新(@RefreshScope)
- 配置变更监听
- 配置版本管理
- 配置回滚
- 共享配置
六、链路追踪
解决问题:请求跨多个服务,如何追踪调用链路?
Sleuth + Zipkin
Spring Cloud 提供的分布式追踪解决方案。
spring:
zipkin:
base-url: http://localhost:9411
sleuth:
sampler:
probability: 1.0@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/{id}")
public Order getOrder(@PathVariable Long id) {
return orderService.getById(id);
}
}在 Zipkin 控制台,你可以看到这样的调用链路:
[order-service]
├─► [user-service]
│ └─► [database]
├─► [product-service]
│ └─► [redis cache]
└─► [payment-service]
└─► [third-party-api]七、消息总线
解决问题:配置变更后,如何让所有服务自动刷新?
Spring Cloud Bus
基于 MQ 的消息广播,用于通知配置变更。
# application.yml
spring:
cloud:
bus:
enabled: true
trace:
enabled: true
rabbitmq:
host: localhost
port: 5672# 触发所有服务刷新配置
curl -X POST http://localhost:8848/nacos/v1/cs/configs?dataId=order-service.yaml&group=DEFAULT_GROUP&content=...八、分布式事务
解决问题:跨服务的数据一致性如何保证?
Seata
阿里巴巴开源的分布式事务解决方案,支持 AT、TCC、Saga 模式。
@GlobalTransactional
public void createOrder(OrderDTO orderDTO) {
// 1. 创建订单
Order order = new Order();
order.setUserId(orderDTO.getUserId());
order.setAmount(orderDTO.getAmount());
orderMapper.insert(order);
// 2. 扣减库存(远程调用)
productClient.deductStock(orderDTO.getProductId(), orderDTO.getQuantity());
// 3. 扣减余额(远程调用)
accountClient.deductBalance(orderDTO.getUserId(), orderDTO.getAmount());
}技术选型建议
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 注册中心 | Nacos | 功能全面,配置中心合一 |
| 配置中心 | Nacos Config | 与注册中心统一,维护成本低 |
| 网关 | Gateway | 异步非阻塞,性能优秀 |
| 服务调用 | OpenFeign | 声明式调用,开发效率高 |
| 限流熔断 | Sentinel | 功能完善,控制台友好 |
| 链路追踪 | Sleuth + Zipkin | 集成简单,可视化清晰 |
| 分布式事务 | Seata | AT 模式对业务无侵入 |
总结
Spring Cloud 全家桶解决了微服务架构的四大核心问题:
- 服务通信:OpenFeign 让服务调用像本地方法一样简单
- 服务治理:Nacos 让服务注册、配置、发现一体化
- 流量控制:Sentinel 提供了全方位的流量防护
- 问题定位:Sleuth + Zipkin 让分布式追踪成为可能
没有银弹。微服务架构带来了灵活性,也带来了复杂度。Spring Cloud 全家桶能帮你降低这种复杂度,但前提是你理解每个组件的设计初衷和适用场景。
