Spring Cloud 概述

什么是 Spring Cloud

Spring Cloud 是基于 Spring Boot 的微服务架构开发工具集,为开发者提供了在分布式系统(如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态)中快速构建一些常见模式的工具。

  • 核心功能
    • 服务注册与发现:自动管理服务实例
    • 配置中心:集中管理配置文件
    • 负载均衡:客户端负载均衡
    • 熔断器:服务容错和降级
    • API 网关:统一入口和路由
    • 消息驱动:事件驱动架构
  • 主要优势
    • 开箱即用:基于 Spring Boot,快速启动
    • 生态完善:涵盖微服务开发的各个方面
    • 社区活跃:大量生产实践案例
    • 灵活扩展:可替换各个组件的实现

Spring Cloud 的核心概念

微服务架构

将单体应用拆分为多个小型、独立部署的服务,每个服务专注于单一职责。

1
2
3
传统单体架构 vs 微服务架构:
单体: 所有功能在一个应用中,耦合度高,扩展困难
微服务: 功能拆分为多个服务,独立部署,易于扩展和维护

服务治理

管理和协调微服务之间的通信和依赖关系。

组件 功能 代表实现
服务注册与发现 管理服务实例的注册和发现 Eureka, Nacos, Consul
配置中心 集中管理配置信息 Config Server, Nacos Config
负载均衡 分散请求到多个服务实例 Ribbon, LoadBalancer
熔断器 防止雪崩效应,服务降级 Hystrix, Sentinel, Resilience4j
API 网关 统一入口,路由转发 Zuul, Gateway
消息总线 配置刷新通知 Bus, RocketMQ

Spring Cloud 的工作原理

服务调用流程

1
2
3
4
5
6
1. 服务提供者启动时向注册中心注册
2. 服务消费者从注册中心获取服务列表
3. 通过负载均衡选择具体实例
4. 发起远程调用(REST/RPC)
5. 熔断器监控调用状态
6. 异常时触发降级逻辑

配置刷新流程

1
2
3
4
5
1. 修改配置中心的配置
2. 发送刷新通知到消息总线
3. 各服务实例监听消息总线
4. 接收到通知后重新加载配置
5. 无需重启服务即可生效

环境搭建

版本对应关系

Spring Cloud 与 Spring Boot 有严格的版本对应关系。

Spring Cloud Spring Boot
2022.0.x (Kilburn) 3.0.x
2021.0.x (Jubilee) 2.6.x
2020.0.x (Ilford) 2.4.x
Hoxton 2.2.x, 2.3.x
Greenwich 2.1.x

最新稳定版:推荐使用 Spring Cloud 2022.0.x + Spring Boot 3.0.x

父工程配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>cloud-parent</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>

<properties>
<java.version>17</java.version>
<spring-boot.version>3.0.0</spring-boot.version>
<spring-cloud.version>2022.0.0</spring-cloud.version>
</properties>

<dependencyManagement>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

<!-- Spring Cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

服务注册与发现

Eureka

添加依赖

1
2
3
4
5
6
7
8
9
10
11
<!-- Eureka Server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

<!-- Eureka Client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

Eureka Server

启动类

1
2
3
4
5
6
7
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}

配置文件

1
2
3
4
5
6
7
8
9
10
11
server:
port: 8761

eureka:
instance:
hostname: localhost
client:
register-with-eureka: false # 不注册自己
fetch-registry: false # 不拉取注册表
server:
enable-self-preservation: false # 关闭自我保护(开发环境)

Eureka Client

启动类

1
2
3
4
5
6
7
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
server:
port: 8081

spring:
application:
name: user-service

eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true # 使用 IP 注册

Nacos(推荐)

添加依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2022.0.0.0</version>
</dependency>

启动类

1
2
3
4
5
6
7
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 8081

spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: dev # 命名空间
group: DEFAULT_GROUP

安装 Nacos

1
2
3
4
5
6
7
8
9
10
11
12
13
# 下载 Nacos
wget https://github.com/alibaba/nacos/releases/download/2.2.0/nacos-server-2.2.0.tar.gz

# 解压
tar -xzf nacos-server-2.2.0.tar.gz

# 启动(单机模式)
cd nacos/bin
sh startup.sh -m standalone

# 访问控制台
# http://localhost:8848/nacos
# 默认用户名/密码:nacos/nacos

服务调用

RestTemplate + LoadBalancer

配置 RestTemplate

1
2
3
4
5
6
7
8
9
@Configuration
public class RestTemplateConfig {

@Bean
@LoadBalanced // 启用负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

服务调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class OrderService {

@Autowired
private RestTemplate restTemplate;

public Order createOrder(Order order) {
// 通过服务名调用
String url = "http://user-service/users/" + order.getUserId();
User user = restTemplate.getForObject(url, User.class);

// 业务逻辑
order.setUserName(user.getName());
return orderRepository.save(order);
}
}

OpenFeign(推荐)

添加依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

启用 Feign

1
2
3
4
5
6
7
@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}

定义 Feign 接口

1
2
3
4
5
6
7
8
9
@FeignClient(name = "user-service")
public interface UserClient {

@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Integer id);

@PostMapping("/users")
User createUser(@RequestBody User user);
}

使用 Feign 客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class OrderService {

@Autowired
private UserClient userClient;

public Order createOrder(Order order) {
// 直接调用接口,像调用本地方法一样
User user = userClient.getUserById(order.getUserId());

order.setUserName(user.getName());
return orderRepository.save(order);
}
}

Feign 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
feign:
client:
config:
user-service: # 服务名
connectTimeout: 5000
readTimeout: 5000
loggerLevel: FULL # 日志级别
compression:
request:
enabled: true
mime-types: text/xml,application/xml,application/json
min-request-size: 2048
response:
enabled: true

配置中心

Spring Cloud Config

Config Server

添加依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>

启动类

1
2
3
4
5
6
7
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
server:
port: 8888

spring:
cloud:
config:
server:
git:
uri: https://github.com/example/config-repo.git
search-paths: configs
username: your-username
password: your-password

Config Client

添加依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>

配置文件(bootstrap.yml)

1
2
3
4
5
6
7
8
spring:
application:
name: user-service
cloud:
config:
uri: http://localhost:8888
profile: dev
label: main

动态刷新配置

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@RefreshScope // 支持动态刷新
public class ConfigController {

@Value("${app.message:Hello}")
private String message;

@GetMapping("/message")
public String getMessage() {
return message;
}
}

Nacos Config(推荐)

添加依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2022.0.0.0</version>
</dependency>

配置文件(bootstrap.yml)

1
2
3
4
5
6
7
8
9
10
spring:
application:
name: user-service
cloud:
nacos:
config:
server-addr: localhost:8848
file-extension: yaml
namespace: dev
group: DEFAULT_GROUP

动态刷新

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
@RefreshScope
public class ConfigController {

@Value("${app.message:Hello}")
private String message;

@GetMapping("/message")
public String getMessage() {
return message;
}
}

负载均衡

Spring Cloud LoadBalancer

添加依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

负载均衡策略

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class LoadBalancerConfig {

@Bean
public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(
Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
name
);
}
}

内置策略

策略 说明
RoundRobinLoadBalancer 轮询(默认)
RandomLoadBalancer 随机
WeightedResponseTimeLoadBalancer 加权响应时间

熔断器

Sentinel(推荐)

添加依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2022.0.0.0</version>
</dependency>

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080 # Sentinel 控制台地址
datasource:
ds1:
nacos:
server-addr: localhost:8848
data-id: ${spring.application.name}-sentinel-rules
group-id: DEFAULT_GROUP
rule-type: flow

流控规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class UserService {

@SentinelResource(value = "getUserById", blockHandler = "handleBlock")
public User getUserById(Integer id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("用户不存在"));
}

// 降级处理方法
public User handleBlock(Integer id, BlockException ex) {
log.warn("触发限流或降级: {}", id);
return new User(); // 返回默认值
}
}

安装 Sentinel

1
2
3
4
5
6
7
8
9
# 下载 Sentinel
wget https://github.com/alibaba/Sentinel/releases/download/1.8.6/sentinel-dashboard-1.8.6.jar

# 启动
java -jar sentinel-dashboard-1.8.6.jar

# 访问控制台
# http://localhost:8080
# 默认用户名/密码:sentinel/sentinel

Resilience4j

添加依赖

1
2
3
4
5
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.7.1</version>
</dependency>

熔断器配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class UserService {

@CircuitBreaker(name = "userService", fallbackMethod = "fallback")
public User getUserById(Integer id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("用户不存在"));
}

public User fallback(Integer id, Throwable t) {
log.error("服务调用失败: {}", t.getMessage());
return new User();
}
}
1
2
3
4
5
6
7
8
resilience4j:
circuitbreaker:
instances:
userService:
sliding-window-size: 10
failure-rate-threshold: 50
wait-duration-in-open-state: 10s
permitted-number-of-calls-in-half-open-state: 5

API 网关

Spring Cloud Gateway

添加依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

基本配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
server:
port: 8080

spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- StripPrefix=1

- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=1

自定义过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Component
public class AuthFilter implements GlobalFilter, Ordered {

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String token = request.getHeaders().getFirst("Authorization");

if (token == null || !token.startsWith("Bearer ")) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}

// 验证 Token
// ...

return chain.filter(exchange);
}

@Override
public int getOrder() {
return -1; // 优先级
}
}

限流配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@ipKeyResolver}"
1
2
3
4
5
6
7
8
9
10
@Configuration
public class RateLimiterConfig {

@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
);
}
}

最佳实践

服务拆分原则

1
2
3
4
5
1. 单一职责:每个服务只负责一个业务领域
2. 独立部署:服务可以独立部署和升级
3. 数据隔离:每个服务有自己的数据库
4. 轻量通信:使用 REST 或 gRPC 进行通信
5. 容错设计:考虑服务失败的情况

配置管理

1
2
3
4
5
6
7
8
9
# 使用环境变量敏感信息
spring:
datasource:
password: ${DB_PASSWORD}

# 不同环境使用不同配置文件
spring:
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}

日志追踪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 使用 MDC 记录链路 ID
@Component
public class TraceIdFilter implements GlobalFilter, Ordered {

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String traceId = UUID.randomUUID().toString();
MDC.put("traceId", traceId);

ServerHttpResponse response = exchange.getResponse();
response.getHeaders().add("X-Trace-Id", traceId);

return chain.filter(exchange).doFinally(signalType -> MDC.clear());
}

@Override
public int getOrder() {
return -1;
}
}

监控告警

1
2
3
4
5
<!-- Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
1
2
3
4
5
6
7
8
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always

常见问题

服务注册失败

1
2
3
4
5
6
7
8
9
10
11
问题:服务无法注册到注册中心

错误原因:
1. 注册中心未启动
2. 网络不通
3. 配置错误

解决方案:
1. 检查注册中心是否正常运行
2. 确认网络连接正常
3. 检查 server-addr 配置是否正确

服务调用超时

1
2
3
4
5
6
7
8
9
问题:Feign 调用超时

解决方案:
1. 增加超时时间
feign.client.config.default.connectTimeout=5000
feign.client.config.default.readTimeout=5000

2. 优化服务性能
3. 使用异步调用

报错处理

💗💗 Spring Cloud 报错:No instances available

1
2
3
4
5
6
7
8
9
10
11
12
错误信息:
No instances available for xxx-service

错误原因:
1. 服务未注册
2. 服务名不匹配
3. 注册中心连接失败

解决方案:
1. 检查服务是否正常启动并注册
2. 确认服务名与注册中心一致
3. 检查注册中心配置

💗💗 Spring Cloud 报错:ConnectTimeoutException

1
2
3
4
5
6
7
8
9
10
11
12
错误信息:
ConnectTimeoutException: Connect to xxx timed out

错误原因:
1. 目标服务未启动
2. 网络问题
3. 超时时间设置过短

解决方案:
1. 确认目标服务正常运行
2. 检查网络连接
3. 增加超时时间配置

学习资源

  • 视频
    • 尚硅谷 SpringCloud 教程:https://www.bilibili.com/video/BV1UJc2ezEFU
  • 官方文档
    • Spring Cloud 官方文档:https://spring.io/projects/spring-cloud
    • Spring Cloud Alibaba:https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/zh-cn/index.html
  • 书籍
    • 《Spring Cloud 微服务实战》:翟永超著
    • 《深入理解 Spring Cloud 与微服务构建》:方志朋著
  • 教程
    • Spring Cloud 入门教程:https://www.baeldung.com/category/spring-cloud/
    • Baeldung Spring Cloud 教程:https://www.baeldung.com/category/spring-cloud/
  • 社区
    • Stack Overflow Spring Cloud 标签:https://stackoverflow.com/questions/tagged/spring-cloud
    • Spring 中文社区:https://spring.io/projects