本文系统梳理 Spring Cloud 微服务全家桶的核心组件——注册中心、配置中心、网关、熔断限流、负载均衡、声明式调用、链路追踪,帮助你快速掌握微服务架构的工程实践。
目录
| 章节 | 说明 |
|---|---|
| 微服务核心组件全景 | 各组件的职责与选型 |
| 注册中心:Eureka vs Nacos | 两代注册中心对比 |
| 负载均衡:Ribbon vs LoadBalancer | 客户端负载均衡演进 |
| OpenFeign 声明式客户端 | 动态代理原理与实战 |
| 配置中心:Spring Cloud Config vs Nacos Config | 配置动态刷新 |
| 熔断降级:Sentinel | 内外兼修的服务容错 |
| 微服务网关:Gateway vs Zuul | 路由、谓词、过滤器 |
| 链路追踪:Sleuth + Zipkin | TraceId / SpanId 原理 |
微服务核心组件全景
Spring Cloud 是专为微服务架构设计的"全家桶"框架,与 Spring Boot 分工协作:
- Spring Boot:主内,处理底层柴米油盐(数据访问、RESTful、日志、容器)
- Spring Cloud:主外,提供分布式系统支持(服务发现、配置管理、网关、熔断)
核心组件一览
| 功能 | Netflix 组件(旧) | Spring Cloud Alibaba(新) | Spring 原生 |
|---|---|---|---|
| 注册中心 | Eureka | Nacos | - |
| 配置中心 | Spring Cloud Config | Nacos Config | - |
| 负载均衡 | Ribbon(维护中) | - | Spring Cloud LoadBalancer |
| 熔断降级 | Hystrix(维护中) | Sentinel | Resilience4j |
| 服务调用 | Feign(旧) | - | OpenFeign |
| 网关 | Zuul 1.x | - | Spring Cloud Gateway |
| 链路追踪 | - | - | Sleuth + Zipkin |
| 消息驱动 | - | RocketMQ | Spring Cloud Stream |
| 分布式事务 | - | Seata | - |
选型建议:新项目优先选用 Spring Cloud Alibaba 组件(Nacos + Sentinel)+ Spring 原生组件(Gateway + LoadBalancer + OpenFeign),避开已进入维护状态的 Netflix 组件(Hystrix、Ribbon)。
注册中心:Eureka vs Nacos
为什么需要注册中心
微服务集群中服务实例的 IP 动态变化,调用方需要通过注册中心实时获取可用节点列表。
核心机制:
Provider 启动 → 向注册中心注册(服务名 + IP:Port + 健康状态)
Consumer 启动 → 订阅注册中心 → 缓存节点列表到本地
节点变更 → 注册中心推送变更给所有订阅方
Eureka vs Nacos 对比
| 维度 | Eureka | Nacos |
|---|---|---|
| 一致性模型 | AP(最终一致) | 支持 AP 和 CP,默认 AP |
| 健康检查 | 客户端心跳(30s) | 客户端心跳 + 服务端主动探测 |
| 服务下线感知 | 慢(心跳超时 90s) | 快(主动推送) |
| 配置中心 | 不支持 | 内置支持 |
| 维护状态 | 2.0 计划搁浅,1.x 维护中 | 活跃维护,持续迭代 |
| 控制台 | 基础 UI | 功能完整的管理控制台 |
| 多数据中心 | 有限支持 | 原生支持 |
Nacos 注册示例
# application.yml
spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: dev # 命名空间隔离
group: DEFAULT_GROUP
// 启动类开启服务发现
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
负载均衡:Ribbon vs LoadBalancer
Spring Cloud 的负载均衡是客户端负载均衡:Consumer 从注册中心获取所有 Provider 节点列表,在本地自主选择节点发起调用,不依赖外部负载均衡设备。
Ribbon vs Spring Cloud LoadBalancer
| 维度 | Ribbon(旧) | Spring Cloud LoadBalancer(新) |
|---|---|---|
| 维护状态 | 进入维护,不推荐新项目使用 | Spring 官方维护,活跃迭代 |
| 响应式支持 | 不支持 | 支持 WebFlux |
| 策略扩展 | 通过 IRule 扩展 | 通过 ReactorLoadBalancer 扩展 |
| 默认策略 | 轮询 | 轮询 |
自定义负载均衡策略(金丝雀测试示例)
// com.example — 自定义负载均衡策略,将带有 canary=true 标签的请求路由到灰度节点
@Bean
public ReactorLoadBalancer<ServiceInstance> canaryLoadBalancer(
Environment environment,
LoadBalancerClientFactory factory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new CanaryLoadBalancer(
factory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
public class CanaryLoadBalancer implements ReactorServiceInstanceLoadBalancer {
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
// 检查请求头中是否有 canary 标记
HttpHeaders headers = ((RequestDataContext) request.getContext())
.getClientRequest().getHeaders();
boolean isCanary = "true".equals(headers.getFirst("X-Canary"));
return serviceInstanceListSupplierProvider.getIfAvailable()
.get(request)
.next()
.map(instances -> selectInstance(instances, isCanary));
}
private Response<ServiceInstance> selectInstance(
List<ServiceInstance> instances, boolean isCanary) {
List<ServiceInstance> targets = instances.stream()
.filter(i -> isCanary == "true".equals(i.getMetadata().get("canary")))
.collect(Collectors.toList());
if (targets.isEmpty()) targets = instances;
return new DefaultResponse(targets.get(ThreadLocalRandom.current()
.nextInt(targets.size())));
}
}
OpenFeign 声明式客户端
OpenFeign 通过动态代理将接口调用转化为远程 HTTP 调用,让服务间调用像调用本地方法一样简单。
动态代理原理
启动阶段:
@EnableFeignClients → 扫包加载所有 @FeignClient 接口
FeignClientsRegistrar → 构造 FeignClientFactoryBean
ReflectiveFeign → 解析注解元数据,生成 MethodHandler
→ 创建实现 InvocationHandler 的动态代理对象,注入 Spring 容器
运行阶段:
业务代码调用 FeignClient 接口方法
→ 动态代理拦截
→ 根据 MethodHandler 中的元数据构建 HTTP Request
→ 通过 LoadBalancer 选择目标节点
→ 发起远程 HTTP 调用
→ 反序列化响应,返回结果
基础使用
// 定义 FeignClient 接口(com.example)
@FeignClient(
value = "order-service", // 服务名(与注册中心一致)
fallback = OrderServiceFallback.class // 降级实现
)
public interface OrderServiceClient {
@GetMapping("/orders/{orderId}")
OrderResponse getOrder(@PathVariable("orderId") Long orderId);
@PostMapping("/orders")
OrderResponse createOrder(@RequestBody CreateOrderRequest request);
@GetMapping("/orders")
List<OrderResponse> listOrders(
@RequestParam("userId") Long userId,
@RequestParam(value = "page", defaultValue = "0") int page
);
}
// 降级实现
@Component
public class OrderServiceFallback implements OrderServiceClient {
@Override
public OrderResponse getOrder(Long orderId) {
return OrderResponse.empty(); // 返回默认值
}
@Override
public OrderResponse createOrder(CreateOrderRequest request) {
throw new ServiceUnavailableException("订单服务暂不可用");
}
@Override
public List<OrderResponse> listOrders(Long userId, int page) {
return Collections.emptyList();
}
}
高级配置
# application.yml
feign:
client:
config:
order-service: # 针对特定服务的配置
connect-timeout: 1000 # 连接超时(ms)
read-timeout: 5000 # 读取超时(ms)
logger-level: FULL # 日志级别:NONE/BASIC/HEADERS/FULL
compression:
request:
enabled: true # 开启请求压缩
min-request-size: 2048
response:
enabled: true # 开启响应压缩
// 自定义 FeignClient 配置
@Configuration
public class FeignConfig {
// 自定义请求拦截器(如添加认证 Token)
@Bean
public RequestInterceptor authInterceptor() {
return template -> {
String token = SecurityContextHolder.getToken();
template.header("Authorization", "Bearer " + token);
};
}
// 自定义重试策略
@Bean
public Retryer feignRetryer() {
// 初始间隔 100ms,最大间隔 1s,最多重试 3 次
return new Retryer.Default(100, 1000, 3);
}
}
配置中心:Spring Cloud Config vs Nacos Config
为什么需要配置中心
微服务数量多,配置分散在各服务中,难以统一管理;配置变更需要重启服务,影响可用性。
配置中心解决:
- 配置集中管理(一处修改,全局生效)
- 动态刷新(不重启服务即可更新配置)
- 环境隔离(dev/test/prod 不同配置)
- 版本管理和回滚
Spring Cloud Config vs Nacos Config
| 维度 | Spring Cloud Config | Nacos Config |
|---|---|---|
| 存储后端 | Git 仓库(主)/ 本地文件 | 内置数据库(MySQL) |
| 动态刷新 | 需配合 Spring Cloud Bus + MQ | 原生支持,长轮询推送 |
| 配置格式 | properties / yaml / json | properties / yaml / json / text |
| 命名空间 | 通过 profile 区分 | 原生命名空间 + Group |
| 控制台 | 无(依赖 Git UI) | 完整管理控制台 |
| 维护活跃度 | 维护中 | 活跃迭代 |
Nacos Config 使用示例
# bootstrap.yml(必须在 bootstrap 中配置,先于 application.yml 加载)
spring:
application:
name: user-service
cloud:
nacos:
config:
server-addr: localhost:8848
namespace: dev
group: DEFAULT_GROUP
file-extension: yaml # 配置文件格式
# 加载多个配置文件
extension-configs:
- data-id: common.yaml
group: COMMON_GROUP
refresh: true
// 动态刷新配置(com.example)
@RestController
@RefreshScope // 标注此注解,配置变更后自动刷新
public class UserController {
@Value("${user.max-page-size:20}")
private int maxPageSize;
@GetMapping("/users")
public List<User> listUsers(@RequestParam int size) {
int actualSize = Math.min(size, maxPageSize);
return userService.list(actualSize);
}
}
熔断降级:Sentinel
服务雪崩与容错思路
服务雪崩:下游服务故障(如慢 SQL 导致响应超时),请求积压,依赖该服务的所有上游服务被拖垮,整个集群崩溃。
Sentinel 的"内外兼修"容错思路:
外部流量控制(限流)
↓ 拦截超量请求,保护服务入口
内部异常治理(降级 + 熔断)
↓ 隔离下游故障,防止级联传播
降级 vs 熔断
| 概念 | 触发条件 | 效果 |
|---|---|---|
| 降级(Fallback) | 单次调用发生异常/超时 | 执行降级逻辑(返回默认值/忽略异常) |
| 熔断(Circuit Breaker) | 异常比例/慢调用比例超过阈值,窗口内降级次数达到阈值 | 一段时间内停止调用下游,所有请求直接走降级逻辑 |
熔断状态机:
关闭(Closed)
↓ 异常比例超阈值
打开(Open)—— 所有请求直接降级
↓ 休眠时间窗口结束
半开(Half-Open)—— 放行少量探测请求
↓ 探测成功 → 关闭;探测失败 → 重新打开
Sentinel 流控规则
// 编程方式定义流控规则(com.example)
@PostConstruct
public void initSentinelRules() {
// 限流规则:QPS 不超过 100
List<FlowRule> flowRules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("getUserById"); // 资源名(接口/方法名)
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(100); // QPS 阈值
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT); // 快速失败
flowRules.add(rule);
FlowRuleManager.loadRules(flowRules);
// 熔断规则:慢调用比例超过 50% 触发熔断
List<DegradeRule> degradeRules = new ArrayList<>();
DegradeRule degradeRule = new DegradeRule();
degradeRule.setResource("getUserById");
degradeRule.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType());
degradeRule.setCount(0.5); // 慢调用比例阈值 50%
degradeRule.setSlowRatioThreshold(0.5);
degradeRule.setTimeWindow(10); // 熔断时长 10s
degradeRules.add(degradeRule);
DegradeRuleManager.loadRules(degradeRules);
}
与 OpenFeign 集成
feign:
sentinel:
enabled: true
@FeignClient(value = "order-service", fallbackFactory = OrderFallbackFactory.class)
public interface OrderServiceClient {
@GetMapping("/orders/{id}")
OrderResponse getOrder(@PathVariable Long id);
}
@Component
public class OrderFallbackFactory implements FallbackFactory<OrderServiceClient> {
@Override
public OrderServiceClient create(Throwable cause) {
return orderId -> {
log.error("调用订单服务失败: {}", cause.getMessage());
return OrderResponse.builder().status("UNAVAILABLE").build();
};
}
}
Resilience4j(Spring 原生替代方案)
// com.example — Resilience4j 熔断器示例
@Service
public class UserService {
@CircuitBreaker(name = "userService", fallbackMethod = "getUserFallback")
@RateLimiter(name = "userService")
@Retry(name = "userService")
public UserResponse getUser(Long userId) {
return externalUserApi.getUser(userId);
}
public UserResponse getUserFallback(Long userId, Exception e) {
log.warn("熔断触发,返回默认用户: userId={}", userId);
return UserResponse.defaultUser(userId);
}
}
resilience4j:
circuitbreaker:
instances:
userService:
failure-rate-threshold: 50 # 失败率 50% 触发熔断
wait-duration-in-open-state: 10s # 熔断持续 10s
sliding-window-size: 10 # 滑动窗口大小
微服务网关:Gateway vs Zuul
Gateway vs Zuul 对比
| 维度 | Spring Cloud Gateway | Zuul 1.x |
|---|---|---|
| 底层模型 | 基于 Netty 的异步非阻塞 | 基于 Servlet 的同步阻塞 |
| 性能 | 高(Reactor 模型) | 相对低(线程池模型) |
| Spring 支持 | Spring 官方推荐 | 逐渐淡出 Spring Cloud |
| WebFlux 支持 | 原生支持 | 不支持 |
| 过滤器 | GlobalFilter + GatewayFilter | ZuulFilter |
结论:新项目统一使用 Spring Cloud Gateway,Zuul 仅用于维护旧系统。
Gateway 核心概念
Route(路由)
├── ID:路由唯一标识
├── URI:目标服务地址(可以是服务名,如 lb://order-service)
├── Predicate(谓词):路由匹配规则,满足所有谓词才转发
└── Filter(过滤器):请求/响应处理链
路由配置示例
# application.yml
spring:
cloud:
gateway:
routes:
# 路由到用户服务
- id: user-service-route
uri: lb://user-service # lb:// 表示通过注册中心做负载均衡
predicates:
- Path=/api/users/** # 路径匹配
- Method=GET,POST # 方法匹配
- Header=X-Version, v2 # Header 匹配
filters:
- StripPrefix=1 # 去掉路径前缀 /api
- AddRequestHeader=X-Gateway-Source, gateway
- name: RequestRateLimiter # 限流过滤器
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
# 限时秒杀路由(只在特定时间段生效)
- id: flash-sale-route
uri: lb://order-service
predicates:
- Path=/api/flash-sale/**
- Between=2024-11-11T00:00:00+08:00, 2024-11-11T23:59:59+08:00
order: 1 # 优先级(数字越小优先级越高)
自定义全局过滤器
// com.example — 全局鉴权过滤器
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String token = request.getHeaders().getFirst("Authorization");
if (isPublicPath(request.getPath().value())) {
return chain.filter(exchange); // 白名单路径直接放行
}
if (token == null || !jwtUtils.validate(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 将用户信息传递给下游服务
String userId = jwtUtils.getUserId(token);
ServerHttpRequest mutatedRequest = request.mutate()
.header("X-User-Id", userId)
.build();
return chain.filter(exchange.mutate().request(mutatedRequest).build());
}
@Override
public int getOrder() {
return -100; // 负数表示高优先级,在其他过滤器之前执行
}
}
全局跨域配置
// com.example — 全局 CORS 配置
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsWebFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOriginPattern("*");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
链路追踪:Sleuth + Zipkin
为什么需要链路追踪
微服务架构中,一次用户请求会经过多个服务,出现 Bug 时难以定位是哪个服务出了问题。链路追踪通过为每次请求生成全局唯一 ID,将所有相关日志串联起来。
Sleuth 核心概念
| 概念 | 说明 |
|---|---|
| Trace | 一次完整的请求调用链,由全局唯一的 Trace ID 标识 |
| Span | 调用链中的一个基本工作单元(如一次 HTTP 调用、一次 DB 查询) |
| Span ID | 当前 Span 的唯一 ID |
| Parent Span ID | 父级 Span 的 ID,通过此字段串联调用顺序 |
日志示例:
# 格式:[服务名, TraceId, SpanId, 是否上报Zipkin]
INFO [user-service, a1b2c3d4e5f6, a1b2c3d4e5f6, true] 收到请求 GET /users/123
INFO [order-service, a1b2c3d4e5f6, f1e2d3c4b5a6, true] 查询用户订单
INFO [payment-service, a1b2c3d4e5f6, 1a2b3c4d5e6f, true] 处理支付
调用链示意:
用户请求
│ Trace ID: a1b2c3
│
├── user-service (Span: A1, Parent: null)
│ │
│ ├── order-service (Span: B2, Parent: A1)
│ │ │
│ │ └── payment-service (Span: C3, Parent: B2)
│ │
│ └── inventory-service (Span: B3, Parent: A1)
Sleuth 四种事件(Annotation)
| 事件 | 缩写 | 含义 |
|---|---|---|
| Client Send | cs | 客户端发送请求 |
| Server Receive | sr | 服务端收到请求 |
| Server Send | ss | 服务端发送响应 |
| Client Receive | cr | 客户端收到响应 |
时延计算:
- 服务端处理时间 = ss - sr
- 网络延迟 = sr - cs(或 cr - ss)
- 总耗时 = cr - cs
集成配置
<!-- pom.xml -->
<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>
# application.yml
spring:
zipkin:
base-url: http://localhost:9411 # Zipkin Server 地址
sender:
type: web # 通过 HTTP 上报(也可用 Kafka/RabbitMQ)
sleuth:
sampler:
probability: 1.0 # 采样率:1.0 = 100%(生产环境建议 0.1)
完整链路追踪方案
微服务日志(含 TraceId/SpanId)
↓ Logback/Log4j2 输出
↓
ELK Stack(日志收集与检索)
├── Logstash:收集和解析日志
├── Elasticsearch:存储和索引日志
└── Kibana:日志查询 UI
Zipkin(调用链可视化)
├── 收集 Span 数据
├── 展示调用链瀑布图
└── 统计各服务耗时
排查流程:
- 用户反馈报错 → 从前端日志/用户行为获取大致时间范围
- 在 Kibana 中搜索该时间段的 ERROR 日志 → 获取 Trace ID
- 在 Zipkin 中输入 Trace ID → 查看完整调用链和各节点耗时
- 定位到出错的服务和 Span → 在 Kibana 中搜索该服务的详细日志
参考资料
- 《Spring Cloud 微服务项目实战》— 极客时间,姚秋辰
- Spring Cloud 官方文档:https://spring.io/projects/spring-cloud
- Spring Cloud Alibaba:https://github.com/alibaba/spring-cloud-alibaba
- Sentinel 官方文档:https://sentinelguard.io/zh-cn/
- Zipkin 官方文档:https://zipkin.io/
评论 (0)
发表评论