Skip to content

第64课:Spring Cloud 基础

🎯 学习目标

  • 理解单体架构和微服务架构的差异,以及微服务带来的收益和复杂度。
  • 掌握服务注册发现、服务调用、负载均衡、配置中心和网关的基本作用。
  • 能使用 Nacos、OpenFeign、Spring Cloud LoadBalancer、Gateway 搭建基本微服务调用链。
  • 能识别微服务拆分过度、远程调用失败、配置漂移、网关职责膨胀等问题。
  • 建立“先单体清晰,再微服务拆分”的工程判断。

📖 一、为什么需要微服务

单体应用:

text
用户、订单、库存、支付都在一个应用里。
统一部署,共享数据库,开发简单。

微服务:

text
用户服务、订单服务、库存服务、支付服务独立部署。
服务之间通过 HTTP、RPC 或消息通信。

微服务优点:

text
独立部署
独立扩展
团队边界清晰
故障隔离更容易
技术栈可局部演进

微服务代价:

text
远程调用失败
分布式事务
链路追踪
配置管理
服务治理
部署和监控复杂度上升

不要为了“架构高级”过早拆微服务。业务边界不清晰时,拆出来只会变成分布式单体。


📖 二、Spring Cloud 组件图

常见组合:

text
Nacos Discovery      服务注册发现
Nacos Config         配置中心
OpenFeign            声明式 HTTP 调用
LoadBalancer         客户端负载均衡
Spring Cloud Gateway API 网关
Sentinel/Resilience4j 限流熔断
Micrometer/Tracing   指标与链路追踪

一个请求链路:

text
Client -> Gateway -> user-service -> order-service -> storage-service

每一跳都可能失败,因此必须考虑超时、重试、降级和日志追踪。


📖 三、服务注册发现

依赖示例:

xml
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

配置:

yaml
spring:
  application:
    name: user-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

启动类:

java
@SpringBootApplication
@EnableDiscoveryClient
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}

服务启动后会把自己的地址注册到 Nacos。调用方通过服务名发现实例,而不是写死 IP。


📖 四、OpenFeign 服务调用

启用 Feign:

java
@SpringBootApplication
@EnableFeignClients
public class UserApplication {
}

声明客户端:

java
@FeignClient(name = "order-service", path = "/api/orders")
public interface OrderClient {

    @GetMapping("/users/{userId}")
    List<OrderResponse> getByUserId(@PathVariable Long userId);

    @PostMapping
    OrderResponse create(@RequestBody OrderCreateRequest request);
}

使用:

java
@Service
public class UserQueryService {
    private final OrderClient orderClient;

    public UserDetailResponse getUserDetail(Long userId) {
        UserResponse user = userService.getById(userId);
        List<OrderResponse> orders = orderClient.getByUserId(userId);
        return UserDetailResponse.of(user, orders);
    }
}

Feign 的本质是声明式 HTTP 客户端。接口调用看起来像本地方法,实际是远程网络调用,必须设置超时和失败处理。


📖 五、负载均衡

order-service 有多个实例:

text
order-service 10.0.0.1:8080
order-service 10.0.0.2:8080
order-service 10.0.0.3:8080

调用方通过服务名调用,LoadBalancer 选择一个实例。

常见配置:

yaml
spring:
  cloud:
    loadbalancer:
      retry:
        enabled: true

注意:重试不是越多越好。写接口、下单接口、扣库存接口等非幂等操作不能随意重试。


📖 六、配置中心

Nacos Config 配置:

yaml
spring:
  application:
    name: user-service
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml

配置中心价值:

text
集中管理多环境配置
动态调整部分配置
避免每个服务打包不同配置
配置变更有审计和回滚

敏感配置仍要谨慎管理。配置中心不是密钥系统的完全替代品。


📖 七、Spring Cloud Gateway

Gateway 是微服务入口。

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/users/**
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/orders/**

常见职责:

text
统一路由
认证前置
限流
跨域
灰度
请求日志
协议转换

不要把复杂业务逻辑写到网关。网关应该薄,业务规则留在服务内部。


⚠️ 八、常见陷阱

1. 拆分过早

团队还无法维护单体清晰边界时,拆微服务会放大复杂度。

2. Feign 当本地调用

远程调用会超时、失败、返回不一致。必须设置超时、降级和日志。

3. 重试导致重复写

非幂等接口随意重试可能创建重复订单或重复扣款。

4. 配置中心无权限控制

配置泄露可能导致数据库密码、密钥泄露。

5. 网关职责膨胀

网关不是业务聚合层。复杂业务编排应该在后端服务或 BFF 中完成。


🆚 九、Java vs C 对比

维度C 分布式服务Spring Cloud
服务发现手写注册或依赖外部库DiscoveryClient
调用客户端手写 HTTP/RPCOpenFeign
负载均衡自定义策略LoadBalancer
网关Nginx/自研网关Spring Cloud Gateway
配置文件/环境变量配置中心

Spring Cloud 把微服务治理能力框架化,但分布式系统的失败模式不会消失。


💡 十、最佳实践

  • 先把单体边界设计清楚,再拆微服务。
  • 服务拆分按业务能力,而不是按数据库表。
  • 所有远程调用必须有超时。
  • 写接口必须考虑幂等。
  • 网关只做入口治理,不承载复杂业务。
  • 配置中心要有权限、审计和回滚机制。
  • 服务间调用日志必须带 traceId。
  • 微服务上线前必须有监控、告警和链路追踪。

🔍 十一、自测问题

text
微服务解决什么问题,又引入什么问题?
服务注册发现的作用是什么?
OpenFeign 为什么不是本地方法调用?
负载均衡和服务发现是什么关系?
配置中心适合管理哪些配置?
网关应该做什么,不应该做什么?
为什么非幂等接口不能随意重试?
什么情况下不应该拆微服务?

🧭 十二、微服务拆分检查清单

拆服务前先确认:

text
业务边界是否清楚?
数据归属是否清楚?
团队是否能独立维护服务?
是否有自动化部署?
是否有链路追踪?
是否有统一日志和指标?
远程调用是否有超时和降级?
接口是否幂等?
是否能接受分布式一致性复杂度?

如果这些基础设施都没有,优先改进单体结构,而不是直接拆服务。


🧪 十三、实战案例:用户详情聚合

一个典型聚合接口:

text
用户服务查询用户基本信息。
订单服务查询最近订单。
优惠券服务查询可用优惠券。
网关或 BFF 聚合返回给前端。

风险:

text
任一下游慢都会拖慢聚合接口。
下游失败时要决定整体失败还是部分降级。
多个服务返回的数据可能不是同一时刻的一致快照。

这说明微服务设计必须同时考虑接口语义和失败策略。


📌 十四、学习建议

建议用三个小服务练习:

text
user-service
order-service
gateway

先跑通注册发现和 Feign 调用,再故意关闭 order-service,观察 user-service 如何失败。这个实验能帮助理解远程调用和本地调用的本质区别。


📚 十五、微服务基础术语

text
服务注册:服务启动后把地址上报注册中心。
服务发现:调用方从注册中心获取可用实例。
负载均衡:从多个实例中选择一个。
网关:统一入口和路由。
配置中心:集中管理配置。
熔断:下游异常时快速失败。
降级:返回兜底结果。
链路追踪:串起跨服务请求。

这些术语不是孤立的,它们共同解决“服务变多后如何治理”的问题。


📌 十六、接口设计建议

微服务接口要明确:

text
是否幂等。
超时时间。
错误码。
是否允许重试。
是否有降级结果。
数据是否最终一致。

远程接口不是本地方法,接口契约必须更严格。

接口文档中应写明错误码、超时语义和是否可重试。调用方不能靠猜测处理失败。

服务之间的契约越明确,故障时越容易快速定位责任边界。

微服务接口还应记录负责人。 核心接口应有变更公告。 废弃接口要保留兼容窗口。


🎓 小结

Spring Cloud 基础不是几个注解和配置,而是一套服务治理模型。服务注册、调用、负载均衡、配置中心和网关构成了微服务基础链路。真正难点是处理远程调用失败、配置一致性、链路追踪和服务边界。