Appearance
Spring Cloud 学习指南
导航目录
- 一、Spring Cloud 概述
- 二、微服务基础与核心问题
- 三、Spring Cloud 核心组件
- 四、服务注册与发现
- 五、服务调用与负载均衡
- 六、配置中心与动态刷新
- 七、服务熔断、限流与降级
- 八、网关、链路追踪与分布式事务
- 九、Spring Cloud Alibaba 实战整合
- 十、面试重点与最佳实践
一、Spring Cloud 概述
1.1 什么是 Spring Cloud
Spring Cloud 是一套基于 Spring Boot 的微服务开发生态,用来解决分布式系统中的常见问题。
- 服务注册与发现
- 服务调用
- 配置中心
- 熔断限流
- 网关路由
- 链路追踪
- 分布式事务
重点:Spring Cloud 不是单个框架,而是一整套微服务解决方案。
1.2 Spring Cloud 和 Spring Boot 的关系
- Spring Boot 用于快速创建单体服务或单个微服务
- Spring Cloud 用于管理多个微服务之间的协作
可以简单理解为:
- Spring Boot 负责“单个服务怎么开发”
- Spring Cloud 负责“多个服务怎么协作”
1.3 为什么需要 Spring Cloud
微服务拆分后会带来新的复杂度:
- 服务数量变多
- 服务地址动态变化
- 配置文件分散
- 服务调用链变长
- 单点故障风险提高
- 运维复杂度上升
Spring Cloud 的目标就是统一解决这些问题。
1.4 Spring Cloud 常见版本体系
常见技术组合:
- Spring Boot
- Spring Cloud
- Spring Cloud Alibaba
版本一定要匹配,否则容易出现依赖冲突、自动装配失败、配置不生效等问题。
重点:Spring Cloud 项目最容易踩坑的地方之一就是版本兼容。
1.5 Spring Cloud 生态分层
text
业务服务层:user-service / order-service / pay-service
治理组件层:Nacos / Gateway / Sentinel / OpenFeign / Seata
基础设施层:MySQL / Redis / MQ / Docker / Kubernetes1.6 Spring Cloud 常见技术栈
| 能力 | 常见组件 |
|---|---|
| 注册中心 | Eureka、Nacos、Consul |
| 配置中心 | Config、Nacos、Apollo |
| 服务调用 | OpenFeign、RestTemplate |
| 负载均衡 | Spring Cloud LoadBalancer |
| 熔断限流 | Sentinel、Resilience4j |
| 网关 | Spring Cloud Gateway |
| 分布式事务 | Seata |
| 链路追踪 | Sleuth、Zipkin、SkyWalking |
1.7 学习路线建议
从入门到精通建议按这个顺序学习:
- 微服务基础概念
- 注册中心
- 服务调用
- 配置中心
- 网关
- 熔断限流
- 链路追踪
- 分布式事务
二、微服务基础与核心问题
2.1 什么是微服务
微服务是一种架构风格,它把大型系统拆成多个独立部署、独立开发、独立运行的小服务。
例如一个电商系统可以拆成:
- 用户服务
- 商品服务
- 订单服务
- 支付服务
- 库存服务
2.2 微服务的优点
- 业务拆分清晰
- 团队协作效率更高
- 支持独立部署
- 支持按需扩容
- 故障隔离更灵活
2.3 微服务的缺点
- 系统复杂度上升
- 运维成本增加
- 网络调用变多
- 数据一致性更难处理
- 排查问题更复杂
2.4 微服务架构中的核心问题
微服务不是只把项目拆开就结束了,还要解决下面这些核心问题:
2.4.1 服务实例如何找到
需要注册中心。
2.4.2 服务之间如何调用
需要 OpenFeign、LoadBalancer 等组件。
2.4.3 多个服务配置如何统一管理
需要配置中心。
2.4.4 某个服务挂了怎么办
需要熔断、降级、限流机制。
2.4.5 外部请求如何统一进入系统
需要网关。
2.4.6 多服务事务如何保证
需要分布式事务方案。
2.5 单体架构和微服务架构对比
| 对比项 | 单体架构 | 微服务架构 |
|---|---|---|
| 部署方式 | 整体部署 | 独立部署 |
| 技术栈 | 通常统一 | 可局部差异 |
| 扩容 | 整体扩容 | 按服务扩容 |
| 故障影响 | 影响整体 | 可局部隔离 |
| 开发复杂度 | 低 | 高 |
| 运维复杂度 | 低 | 高 |
2.6 微服务设计原则
- 高内聚、低耦合
- 服务边界清晰
- 接口稳定
- 配置外部化
- 无状态优先
三、Spring Cloud 核心组件
3.1 Spring Cloud 的核心能力图
text
服务注册与发现 -> Nacos / Eureka
服务调用 -> OpenFeign / LoadBalancer
配置中心 -> Nacos Config / Spring Cloud Config
服务保护 -> Sentinel / Resilience4j
网关 -> Spring Cloud Gateway
链路追踪 -> Sleuth / SkyWalking
事务协调 -> Seata3.2 常见组件说明
3.2.1 Nacos
- 服务注册与发现
- 配置管理
3.2.2 OpenFeign
- 声明式 HTTP 调用
- 简化远程服务调用代码
3.2.3 LoadBalancer
- 基于服务名做客户端负载均衡
3.2.4 Gateway
- 路由转发
- 统一鉴权
- 限流过滤
3.2.5 Sentinel
- 熔断
- 限流
- 降级
- 热点参数保护
3.2.6 Seata
- 分布式事务协调
3.3 Spring Cloud 和 Spring Cloud Alibaba
Spring Cloud Alibaba 是对 Spring Cloud 的补充和增强。
常见替代关系:
- Nacos 替代 Eureka + Config
- Sentinel 替代 Hystrix 部分能力
- RocketMQ / Dubbo / Seata 与 Spring Cloud 深度集成
重点:现在国内 Java 微服务项目里,Spring Cloud + Spring Cloud Alibaba 是非常常见的组合。
四、服务注册与发现
4.1 为什么需要注册中心
在微服务环境里,服务实例会不断变化:
- 服务可能扩容
- 服务可能缩容
- 服务可能重启
- 服务地址可能改变
如果调用方写死 IP 和端口,系统就无法灵活伸缩。
4.2 注册中心工作流程
- 服务提供者启动
- 向注册中心注册实例
- 消费者从注册中心获取服务列表
- 负载均衡组件选择一个实例发起调用
4.3 Nacos 注册中心配置示例
yaml
server:
# 当前服务端口
port: 8001
spring:
application:
# 注册到注册中心中的服务名
name: user-service
cloud:
nacos:
# Nacos 服务端地址
server-addr: 127.0.0.1:8848
discovery:
# 注册中心命名空间,用于环境隔离
namespace: dev-namespace-id
# 服务分组
group: DEFAULT_GROUP
# 集群名称
cluster-name: HZ4.4 服务发现代码示例
java
@RestController
@RequestMapping("/discovery")
public class DiscoveryController {
@Resource
private DiscoveryClient discoveryClient;
@GetMapping("/services")
public List<String> services() {
// 查询注册中心中的所有服务名称
return discoveryClient.getServices();
}
@GetMapping("/instances")
public List<ServiceInstance> instances() {
// 查询 user-service 对应的全部实例
return discoveryClient.getInstances("user-service");
}
}4.5 注册中心学习重点
- 服务注册流程
- 服务发现流程
- 命名空间和分组
- 集群与元数据
- 健康检查机制
五、服务调用与负载均衡
5.1 为什么需要服务调用组件
微服务之间一般通过 HTTP 或 RPC 通信。
常见问题:
- 如何根据服务名找到具体实例
- 如何做负载均衡
- 如何处理超时和异常
5.2 RestTemplate 调用示例
5.2.1 配置类
java
@Configuration
public class RestTemplateConfig {
@Bean
// 开启基于服务名的负载均衡能力
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}5.2.2 调用代码
java
@RestController
@RequestMapping("/order")
public class OrderController {
@Resource
private RestTemplate restTemplate;
@GetMapping("/create")
public String createOrder() {
// 通过服务名调用 user-service,而不是写死 IP 和端口
return restTemplate.getForObject("http://user-service/user/info", String.class);
}
}5.3 OpenFeign 调用示例
5.3.1 引入依赖
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<!-- 声明式远程调用依赖 -->
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>5.3.2 Feign 接口
java
// name 对应注册中心中的服务名
@FeignClient(name = "user-service")
public interface UserFeignClient {
@GetMapping("/user/info")
String getUserInfo();
}5.3.3 返回统一结果对象示例
在真实项目里,远程调用通常不会直接返回字符串,而是返回统一响应对象。
java
@Data
public class Result<T> {
// 业务状态码,200 表示成功
private Integer code;
// 提示信息
private String message;
// 实际业务数据
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("success");
result.setData(data);
return result;
}
public static <T> Result<T> fail(Integer code, String message) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMessage(message);
return result;
}
}java
@Data
public class UserDTO {
private Long id;
private String username;
private String nickname;
}java
// 更推荐的 Feign 返回方式:统一结果对象 + DTO
@FeignClient(name = "user-service")
public interface UserFeignClient {
@GetMapping("/user/{id}")
Result<UserDTO> getUserById(@PathVariable("id") Long id);
}5.3.4 启动类开启 Feign
java
@EnableFeignClients
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
// 启动服务,并启用 Feign 客户端扫描
SpringApplication.run(OrderApplication.class, args);
}
}5.3.5 控制器调用
java
@RestController
@RequestMapping("/feign")
public class FeignController {
@Resource
private UserFeignClient userFeignClient;
@GetMapping("/user")
public String user() {
// 通过 Feign 接口发起远程调用
return userFeignClient.getUserInfo();
}
}java
@RestController
@RequestMapping("/order")
public class OrderQueryController {
@Resource
private UserFeignClient userFeignClient;
@GetMapping("/user/{id}")
public Result<UserDTO> queryUser(@PathVariable Long id) {
// 调用 user-service 查询用户信息
Result<UserDTO> result = userFeignClient.getUserById(id);
// 统一判断远程结果是否成功
if (result == null || result.getCode() != 200) {
return Result.fail(500, "远程调用 user-service 失败");
}
return result;
}
}5.4 OpenFeign 异常处理
5.4.1 为什么 Feign 调用必须做异常处理
远程调用天然不稳定,可能出现:
- 网络超时
- 服务不可用
- 返回 4xx / 5xx
- 下游业务异常
- 注册中心中没有可用实例
重点:本地方法调用失败和远程调用失败是两回事,远程调用必须考虑超时、重试、降级和兜底。
5.4.2 业务层 try-catch 处理示例
java
@Service
public class OrderService {
@Resource
private UserFeignClient userFeignClient;
public UserDTO getUserInfo(Long userId) {
try {
Result<UserDTO> result = userFeignClient.getUserById(userId);
if (result == null || result.getCode() != 200 || result.getData() == null) {
throw new IllegalStateException("查询用户信息失败");
}
return result.getData();
} catch (Exception ex) {
// 这里可以打印日志、打监控埋点、做降级处理
throw new RuntimeException("调用 user-service 异常: " + ex.getMessage(), ex);
}
}
}5.4.3 Feign 默认错误解码器说明
默认情况下:
- 远程返回
2xx,Feign 正常反序列化 - 远程返回
4xx / 5xx,Feign 通常会抛异常
因此不能假设:
- Feign 调用永远有返回值
- Feign 调用失败时只会返回业务码
5.4.4 自定义 ErrorDecoder 示例
java
@Configuration
public class FeignErrorConfig {
@Bean
public ErrorDecoder errorDecoder() {
return (methodKey, response) -> {
// 根据不同 HTTP 状态码封装更明确的异常
if (response.status() == 404) {
return new RuntimeException("远程服务资源不存在: " + methodKey);
}
if (response.status() == 500) {
return new RuntimeException("远程服务内部异常: " + methodKey);
}
return new RuntimeException("Feign 调用异常,状态码: " + response.status());
};
}
}5.4.5 指定 Feign 配置示例
java
@FeignClient(
name = "user-service",
configuration = FeignErrorConfig.class
)
public interface UserFeignClientWithErrorDecoder {
@GetMapping("/user/{id}")
Result<UserDTO> getUserById(@PathVariable("id") Long id);
}5.5 Feign 超时配置示例
yaml
spring:
cloud:
openfeign:
client:
config:
default:
# 建立连接超时时间,单位毫秒
connectTimeout: 3000
# 读取响应超时时间,单位毫秒
readTimeout: 50005.6 Feign 日志配置示例
java
@Configuration
public class FeignLogConfig {
@Bean
public Logger.Level feignLoggerLevel() {
// FULL 会打印请求方法、URL、请求头、请求体、响应信息
return Logger.Level.FULL;
}
}yaml
logging:
level:
# 打开指定 Feign 客户端的日志级别
com.demo.feign.UserFeignClient: debug5.7 OpenFeign 降级与熔断
在 Spring Cloud Alibaba 项目里,OpenFeign 常常和 Sentinel 一起使用实现降级。
5.7.1 开启 Feign Sentinel 支持
yaml
feign:
sentinel:
# 开启 Feign 与 Sentinel 的整合
enabled: true5.7.2 Fallback 降级类示例
java
@Component
public class UserFeignFallback implements UserFeignClient {
@Override
public Result<UserDTO> getUserById(Long id) {
// 当远程服务不可用、超时或被熔断时返回兜底数据
return Result.fail(500, "user-service 当前不可用,触发 fallback");
}
}5.7.3 指定 fallback 的 Feign 接口
java
@FeignClient(name = "user-service", fallback = UserFeignFallback.class)
public interface UserFeignClient {
@GetMapping("/user/{id}")
Result<UserDTO> getUserById(@PathVariable("id") Long id);
}5.7.4 FallbackFactory 示例
相比 fallback,FallbackFactory 能拿到原始异常,更适合排查问题。
java
@Component
public class UserFeignFallbackFactory implements FallbackFactory<UserFeignClient> {
@Override
public UserFeignClient create(Throwable cause) {
return id -> {
// 可以在这里记录原始异常 cause
return Result.fail(500, "user-service 调用失败: " + cause.getMessage());
};
}
}java
@FeignClient(name = "user-service", fallbackFactory = UserFeignFallbackFactory.class)
public interface UserFeignClientWithFactory {
@GetMapping("/user/{id}")
Result<UserDTO> getUserById(@PathVariable("id") Long id);
}重点:生产环境里更推荐 FallbackFactory,因为它能拿到原始异常信息。
5.8 OpenFeign 学习重点
- 声明式远程调用
- 统一返回对象
- 超时和异常处理
- ErrorDecoder
- fallback 和 fallbackFactory
5.9 负载均衡策略理解
LoadBalancer 会从服务实例列表中选择一个实例进行调用。
常见策略理解:
- 轮询
- 随机
- 按权重
- 同集群优先
重点:服务调用的核心不是“调起来”,而是“调得稳、调得准、调得可治理”。
六、配置中心与动态刷新
6.1 为什么需要配置中心
- 配置统一托管
- 环境隔离
- 动态刷新
- 降低重复配置
6.2 Nacos 配置中心依赖
xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<!-- Nacos 配置中心依赖 -->
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>6.3 配置中心示例
yaml
spring:
application:
# 当前服务名
name: order-service
profiles:
# 当前激活环境
active: dev
config:
import:
# 导入共享配置
- optional:nacos:common.yaml?group=COMMON_GROUP&refreshEnabled=true
# 导入当前服务环境配置
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml?group=DEFAULT_GROUP&refreshEnabled=true
cloud:
nacos:
server-addr: 127.0.0.1:8848
config:
# 配置中心命名空间
namespace: dev-namespace-id
# 配置分组
group: ORDER_GROUP6.4 动态刷新示例
java
@RefreshScope
@RestController
@RequestMapping("/config")
public class ConfigController {
@Value("${order.timeout:30}")
private Integer timeout;
@GetMapping("/timeout")
public Integer timeout() {
// 配置变更后,这里的值可以动态刷新
return timeout;
}
}6.5 配置中心学习重点
- Namespace、Group、Data ID
- 共享配置和应用专属配置
- 配置优先级
- 动态刷新
- 环境隔离
七、服务熔断、限流与降级
7.1 为什么需要服务保护
在高并发场景中,一个下游服务异常可能把整个调用链拖垮。
需要保护机制来解决:
- 接口响应慢
- 服务不可用
- 流量突增
- 热点参数被恶意请求
7.2 常见保护手段
7.2.1 限流
- 控制单位时间内的请求量
7.2.2 降级
- 当服务压力过大时,返回兜底结果
7.2.3 熔断
- 当故障比例过高时,临时切断调用链
7.2.4 隔离
- 防止故障扩散
7.3 Sentinel 示例
java
@RestController
@RequestMapping("/sentinel")
public class SentinelController {
@GetMapping("/test")
@SentinelResource(value = "testResource", blockHandler = "blockHandler")
public String test() {
return "success";
}
// 被限流时执行的兜底方法
public String blockHandler(BlockException ex) {
return "request blocked";
}
}7.4 Sentinel 熔断降级说明
Sentinel 常见保护维度:
- 流控规则:限制 QPS、线程数
- 降级规则:按慢调用比例、异常比例、异常数触发熔断
- 热点规则:限制某些热点参数
- 授权规则:控制黑白名单来源
7.5 熔断降级方法示例
java
@RestController
@RequestMapping("/order")
public class OrderSentinelController {
@GetMapping("/detail/{id}")
@SentinelResource(
value = "queryOrderDetail",
blockHandler = "blockHandler",
fallback = "fallbackHandler"
)
public String detail(@PathVariable Long id) {
if (id < 0) {
throw new IllegalArgumentException("id 不能小于 0");
}
return "order detail: " + id;
}
// 被 Sentinel 限流、熔断时进入这个方法
public String blockHandler(Long id, BlockException ex) {
return "当前请求被限流或熔断,id=" + id;
}
// 业务异常时进入这个方法
public String fallbackHandler(Long id, Throwable ex) {
return "查询订单失败,进入 fallback,id=" + id;
}
}7.6 OpenFeign + Sentinel 配合思路
在真实项目里通常这样分工:
- Feign 负责发起远程调用
- Sentinel 负责限流、熔断、降级
- fallback / fallbackFactory 负责兜底返回
7.7 服务保护配置建议
yaml
spring:
cloud:
sentinel:
transport:
# Sentinel 控制台地址
dashboard: 127.0.0.1:8080
eager: true7.8 服务保护学习重点
- 限流
- 降级
- 熔断
- fallback 和 blockHandler 的区别
- Feign 和 Sentinel 联动
八、网关、链路追踪与分布式事务
8.1 为什么需要网关
网关是所有外部请求的统一入口。
网关可以做:
- 路由转发
- 鉴权
- 限流
- 统一日志
- 跨域处理
8.2 为什么需要链路追踪
微服务调用链很长,问题排查需要知道:
- 请求经过了哪些服务
- 哪个服务最慢
- 哪个服务报错
8.3 为什么需要分布式事务
一次业务操作可能同时修改多个服务的数据。
例如:
- 创建订单
- 扣减库存
- 扣减余额
如果中途某一步失败,就要考虑如何回滚。
8.4 Seata 事务协调思路
text
订单服务 -> 库存服务 -> 账户服务
任意一步失败 -> 全局事务回滚重点:微服务拆分带来的最大难题之一就是“分布式一致性”。
九、Spring Cloud Alibaba 实战整合
9.1 常见依赖组合
xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<!-- 统一管理 Spring Cloud Alibaba 依赖版本 -->
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2023.0.1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>xml
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<!-- 注册中心 -->
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<!-- 配置中心 -->
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<!-- 声明式服务调用 -->
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<!-- Sentinel 服务保护 -->
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>9.2 一个完整配置示例
yaml
server:
# 服务端口
port: 8088
spring:
application:
# 服务名
name: mall-order-service
profiles:
# 激活开发环境
active: dev
config:
import:
# 导入共享配置
- optional:nacos:common.yaml?group=COMMON_GROUP&refreshEnabled=true
# 导入当前服务环境配置
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml?group=DEFAULT_GROUP&refreshEnabled=true
cloud:
nacos:
# Nacos 地址
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
config:
# 配置中心命名空间
namespace: dev-namespace-id
group: MALL_GROUP
discovery:
# 注册中心命名空间
namespace: dev-namespace-id
group: MALL_GROUP
# 集群名称
cluster-name: HZ
openfeign:
client:
config:
default:
# Feign 连接超时
connectTimeout: 3000
# Feign 读取超时
readTimeout: 5000
sentinel:
transport:
# Sentinel 控制台地址
dashboard: 127.0.0.1:8080
feign:
sentinel:
# 开启 OpenFeign 和 Sentinel 联动
enabled: true9.3 Spring Cloud Alibaba 推荐组件组合
| 能力 | 推荐组件 |
|---|---|
| 注册中心 | Nacos Discovery |
| 配置中心 | Nacos Config |
| 远程调用 | OpenFeign |
| 负载均衡 | Spring Cloud LoadBalancer |
| 熔断限流 | Sentinel |
| 网关 | Spring Cloud Gateway |
| 分布式事务 | Seata |
9.4 典型项目结构
text
mall-parent
├─ mall-common
├─ mall-gateway
├─ mall-user-service
├─ mall-order-service
├─ mall-product-service
└─ mall-api9.5 一套完整远程调用链路示例
text
order-service -> OpenFeign -> user-service
-> Sentinel 熔断降级
-> fallbackFactory 兜底
-> Nacos 提供服务发现9.6 实战学习重点
- Nacos + OpenFeign
- Nacos + Gateway
- Nacos + Sentinel
- Nacos + Seata
- 多环境配置隔离
十、面试重点与最佳实践
10.1 高频面试题
- Spring Cloud 和 Spring Boot 的区别
- 注册中心为什么必要
- OpenFeign 和 RestTemplate 的区别
- 配置中心为什么重要
- 熔断、降级、限流有什么区别
- 网关的作用是什么
- 分布式事务怎么做
- OpenFeign 异常处理怎么做
- fallback 和 fallbackFactory 有什么区别
10.2 最佳实践
- 按环境划分 Namespace
- 按业务划分 Group
- 共享配置前置,业务配置后置
- 网关统一做鉴权、日志和跨域
- 关键接口必须有限流和降级
- 远程调用必须配置超时、异常处理和兜底
- 统一返回对象,避免跨服务返回结构混乱
- 核心配置要支持回滚
- 生产环境必须开启监控和审计
10.3 总结
Spring Cloud 的核心价值,在于帮助我们把微服务里的常见复杂问题标准化。
- 入门阶段要掌握注册中心、服务调用、配置中心
- 进阶阶段要掌握网关、限流、降级、追踪
- 高阶阶段要掌握事务一致性、治理能力和生产实践