Spring Cloud:微服务的基础设施工具箱

目录

微服务是什么

微服务是一种架构风格:把一个大应用按业务能力拆成多个小服务,每个服务负责一块独立的功能,有自己的代码库、数据库,可以独立部署和扩展。

一个单体应用把用户管理、订单处理、商品展示、支付逻辑全写在一个项目里。功能越来越多,代码库越来越膨胀,改一个模块要重新编译整个项目,发布一次要停服半小时。微服务的做法是把这些功能拆开,用户服务只管用户,订单服务只管订单,各自独立开发、独立发布。

在这里插入图片描述

Spring Cloud 是什么

Spring Cloud 不是一个单独的框架,而是一套微服务基础设施的组件集合。它基于 Spring Boot,把微服务开发中需要的各种工具(注册中心、网关、熔断器、配置中心、链路追踪)整合到一起,提供统一的编程模型和配置方式。

可以把它理解成一个"工具箱"。你装修房子需要电钻、扳手、水平仪、卷尺,你不会每样都去不同厂家买,而是买一个工具箱,里面配齐了常用工具。Spring Cloud 就是微服务的工具箱,你需要什么组件就引入什么 starter,用 Spring Boot 的方式配置和使用。

两者的关系

微服务是一种软件架构思想,它把一个大型单体应用拆分成多个独立的小服务,每个服务负责一个业务能力,可以独立开发、部署和扩展;而 Spring Cloud 是实现微服务架构的一套开发工具集和解决方案,它基于 Spring Boot 提供了服务注册与发现、配置中心、负载均衡、服务调用、熔断降级、网关等微服务所需的基础组件。简单来说,微服务是“怎么设计系统”的理念,Spring Cloud 是“如何用 Java 技术实现微服务”的工具,两者类似于“建筑设计思想”和“施工工具”的关系。你可以不用 Spring Cloud 实现微服务,也可以使用 Spring Cloud 来快速构建微服务系统。

微服务需要解决的问题

在深入组件之前,先把问题理清楚。微服务架构下,开发者需要面对的核心问题和 Spring Cloud 的对应组件如下:

问题说明Spring Cloud 组件
服务发现服务动态扩缩容,调用方怎么找到服务实例Nacos / Eureka
负载均衡多个实例时,请求该发给谁Spring Cloud LoadBalancer
服务调用服务之间怎么通信OpenFeign
熔断降级服务挂了或响应慢,怎么防止雪崩Sentinel / Resilience4j
网关路由所有服务的统一入口在哪Spring Cloud Gateway
配置管理20 个服务的配置怎么集中管理Nacos Config
链路追踪一次请求经过了哪些服务,每个环节耗时多少Micrometer Tracing

这些组件之间是独立的,你可以按需引入。不用全部上,小项目可能只需要 Nacos + OpenFeign 就够了。

核心组件详解

服务注册与发现

先解决最基础的问题:服务之间怎么找到彼此。

传统做法是把服务地址写死在配置文件里(硬编码),服务扩容缩容时要手动改配置。Nacos 作为注册中心,解决了这个问题。

工作方式很直接:每个服务启动时把自己的地址注册到 Nacos,关闭时自动注销。调用方通过服务名去 Nacos 查询目标服务的地址列表,Nacos 返回当前可用的实例。

引入 Nacos 依赖:

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

配置文件:

spring:
  application:
    name: order-service  # 服务名,其他服务通过这个名字找到你
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848  # Nacos 地址

启动类加一个注解就完事了:

@SpringBootApplication
@EnableDiscoveryClient  // 开启服务注册与发现
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

服务启动后会自动注册到 Nacos。你在 Nacos 控制台能看到所有已注册的服务和实例。服务下线时自动注销,不需要手动维护地址列表。

OpenFeign

服务发现了,接下来要解决的是"怎么调用"。订单服务要调用户服务查用户信息,最原始的方式是用 RestTemplate 手动拼 URL:

// 手动调用,又臭又长
String url = "http://user-service/api/user/" + userId;
User user = restTemplate.getForObject(url, User.class);

OpenFeign 把这个过程简化了。你只需要定义一个接口,加几个注解,就能像调本地方法一样调远程服务。

先加依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

然后定义一个接口:

@FeignClient(name = "user-service")  // 指定要调用的服务名
public interface UserClient {

    @GetMapping("/api/user/{id}")
    User getUser(@PathVariable("id") int id);
}

使用时直接注入调用:

@Service
public class OrderService {

    @Autowired
    private UserClient userClient;

    public OrderDetail getOrderDetail(int orderId) {
        Order order = orderMapper.selectById(orderId);
        // 像调本地方法一样调远程服务
        User user = userClient.getUser(order.getUserId());
        return new OrderDetail(order, user);
    }
}

OpenFeign 帮你做了几件事:根据服务名从 Nacos 拿到地址列表,通过负载均衡选一个实例,把方法调用转成 HTTP 请求发出去,把响应 JSON 反序列化成 Java 对象返回。你写的代码里看不到任何网络调用的痕迹。

Spring Cloud Gateway

微服务拆完之后,前端要记 20 个服务地址,这显然不现实。你需要一个统一的入口,所有外部请求先打到网关,网关再转发到对应的服务。这就是 Spring Cloud Gateway 的作用。

网关最核心的工作是路由转发:根据请求路径把请求转发到对应的服务,/api/user/** 转发到用户服务,/api/order/** 转发到订单服务。除此之外,网关还能做限流(防突发流量打挂服务)和鉴权(统一校验 Token,不用每个服务都写一遍)。

配置示例:

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service  # lb 表示走负载均衡
          predicates:
            - Path=/api/user/**   # 匹配路径
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order/**

有了网关,前端只需要记住一个地址,网关负责把请求分发到正确的服务。

Sentinel

服务之间通过网络通信,网络是不可靠的。用户服务响应变慢了,订单服务还在傻等 30 秒超时;订单服务挂了,调用它的服务也跟着卡住。一个服务的问题层层传递,最终拖垮整个系统,这就是服务雪崩

Sentinel 的熔断机制能在检测到下游服务异常时,快速返回一个降级响应,而不是让调用方一直等。

@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient {

    @GetMapping("/api/user/{id}")
    User getUser(@PathVariable("id") int id);
}

// 降级逻辑:用户服务不可用时返回默认值
@Component
public class UserClientFallback implements UserClient {
    @Override
    public User getUser(int id) {
        User user = new User();
        user.setName("用户信息暂时无法获取");
        return user;
    }
}

用户服务挂了,订单服务不会卡住,而是拿到一个降级的用户信息,订单详情页至少能正常展示,只是用户信息那一栏显示"暂时无法获取"。这比整个页面报错好得多。

Nacos Config

20 个服务有各自的配置文件,有些配置是公共的(数据库连接信息、Redis 地址、公共开关)。如果每个服务的 application.yml 里都写一遍,改一个地址要改 20 个文件。Nacos Config 把配置集中管理,服务启动时从 Nacos 拉取配置,运行时还能监听配置变更,实现热更新。

# bootstrap.yml(优先于 application.yml 加载)
spring:
  application:
    name: order-service
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml  # 配置文件格式

在 Nacos 控制台里创建一个 order-service.yaml 配置文件,服务启动时会自动拉取。如果在 Nacos 控制台修改了配置,服务能实时感知到变化,不需要重启。

公共配置可以抽取成一个 common.yaml,通过 shared-configs 引入:

spring:
  cloud:
    nacos:
      config:
        shared-configs:
          - data-id: common.yaml
            group: DEFAULT_GROUP
            refresh: true  # 支持热更新

这样改一次 common.yaml,所有引用它的服务都会生效。

一次请求的完整链路

把上面的组件串起来,看一次完整的请求是怎么流转的:

    用户(浏览器/APP)
        │
        ▼
   Spring Cloud Gateway
        │
        ├── 路由规则匹配
        │   /api/order/** → order-service
        │
        ▼
    order-service
        │
        ├── 从 Nacos Config 拿配置
        │
        ├── 通过 OpenFeign 调 user-service
        │       │
        │       ├── 从 Nacos 拿 user-service 地址列表
        │       ├── LoadBalancer 选一个实例
        │       └── 发 HTTP 请求
        │               │
        │               ▼
        │          user-service
        │               │
        │               ├── 查数据库
        │               └── 返回用户信息
        │
        ├── 如果 user-service 超时 → Sentinel 熔断,返回降级数据
        │
        └── 组装结果返回给 Gateway → 返回给用户

一次请求经过了网关、注册中心、配置中心、负载均衡、熔断器,这些组件各司其职,对业务代码是透明的。你写的 orderService.getOrderDetail(orderId) 这一行代码背后,整个微服务基础设施在默默工作。

和 Dubbo 有什么区别

这是面试和选型时经常被问到的问题。Spring Cloud 和 Dubbo 都是微服务框架,但设计理念完全不同。

维度Spring CloudDubbo
通信协议HTTP/RESTRPC(默认 Dubbo 协议)
序列化JSONHessian2 / Protobuf
服务治理依赖各组件(Nacos、Sentinel 等)内置服务治理能力
语言支持主要 JavaJava 为主,支持多语言
性能JSON 序列化 + HTTP,相对较慢二进制序列化 + TCP,性能更高
学习成本组件多,需要逐个学习一个框架搞定,相对集中
生态Spring 全家桶,组件丰富阿里生态,和 Nacos/Seata 配合好

Spring Cloud 和 Dubbo 代表了两种不同的微服务设计思路。Spring Cloud 更偏向“微服务生态整合”,将服务注册、配置管理、网关、链路追踪等能力拆分成多个组件,功能丰富、扩展灵活,但整体组件较多;Dubbo 则更专注于高性能 RPC 通信和服务治理,将服务调用能力进行了深度封装,调用效率高,但更多依赖自身生态和插件扩展。

在技术选型上,没有绝对的优劣之分。如果熟悉 Spring 生态,并且项目需要完整的微服务治理能力,例如网关、配置中心、监控追踪等,Spring Cloud 会更加合适;如果系统内部服务调用频繁,对通信性能和稳定性要求较高,同时团队具备 Dubbo 使用经验,那么 Dubbo 可能更适合。实际企业项目中,两者也经常结合使用,例如使用 Spring Cloud Gateway 负责外部请求入口治理,内部服务之间通过 Dubbo 进行高性能 RPC 调用。

小结

Spring Cloud 是构建微服务架构的一套基础设施解决方案,它基于 Spring Boot 将服务注册与发现、负载均衡、熔断降级、网关路由、配置管理等微服务治理能力封装成标准化组件。开发者无需重复造轮子,通过引入对应的 Starter 和简单配置,就可以让多个服务实现独立部署、协同通信和统一治理。

但 Spring Cloud 并不是解决所有问题的万能方案。它更适用于服务规模较大、业务复杂、需要完善治理能力的系统。如果项目只有少量服务,架构简单,直接使用普通 HTTP 调用或 RPC 框架可能更加轻量。微服务不是拆得越细越好,技术选型应该结合业务规模和实际需求,避免为了追求“微服务化”而引入不必要的复杂度。

更多推荐