专栏文章
专栏文章
Spring Cloud 系列
1. Spring Cloud 系列 #01:Spring Cloud 微服务

Spring Cloud 系列 #01:Spring Cloud 微服务

发布于 2026-05-26 09:44 👁 12 次阅读
#spring#microservice

本文系统梳理 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 分工协作:

../../../assets/01 Spring Cloud 微服务/file 20260521120650756

核心组件一览

功能 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)。


../../../assets/01 Spring Cloud 微服务/file 20260521120651180

注册中心: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

为什么需要配置中心

微服务数量多,配置分散在各服务中,难以统一管理;配置变更需要重启服务,影响可用性。

配置中心解决:

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 客户端收到响应

时延计算

集成配置

<!-- 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 数据
  ├── 展示调用链瀑布图
  └── 统计各服务耗时

排查流程

  1. 用户反馈报错 → 从前端日志/用户行为获取大致时间范围
  2. 在 Kibana 中搜索该时间段的 ERROR 日志 → 获取 Trace ID
  3. 在 Zipkin 中输入 Trace ID → 查看完整调用链和各节点耗时
  4. 定位到出错的服务和 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)

暂无评论,来留下第一条吧。

发表评论