05微服务保护
微服务架构演进
单体架构中,短信、积分、优惠券、用户、订单、商品、支付、购物车、评价等所有业务模块都集中在一个工程中。随着业务规模扩展,单体架构难以维护和扩展,于是拆分为微服务。

微服务之间通过 OpenFeign 完成远程调用,并通过 Nacos 完成服务治理。在此基础上引入网关后,系统具备请求路由、服务保护、身份认证、分布式事务、配置管理等能力。


雪崩问题
什么是雪崩
在微服务调用链路中,某个服务出现故障或阻塞,引起整个链路中所有微服务都不可用,这就是雪崩。
例如购物车服务调用商品服务时,如果商品服务出现故障,购物车服务的请求会被卡住,持续占用 tomcat 线程资源。随着请求不断累积,购物车服务的 tomcat 资源最终会被耗尽,导致购物车服务也不可用,进而扩散至整个调用链。
雪崩产生的原因
- 微服务相互调用,服务提供者出现故障或阻塞
- 服务调用者没有做好异常处理,导致自身故障
- 调用链中的所有服务级联失败,导致整个集群故障
解决思路
- 尽量避免服务出现故障或阻塞:保证代码健壮性、网络畅通、应对较高并发请求
- 服务调用者做好远程调用异常的后备方案,避免故障扩散
服务保护方案
请求限流
限制访问微服务的请求并发量,避免服务因流量激增出现故障。通过限流器控制 QPS,将流量限制在服务可承受范围。
线程隔离
也叫舱壁模式,模拟船舱隔板的防水原理。通过限定每个业务能使用的线程数量而将故障业务隔离,避免故障扩散。

失败处理(Fallback)
给业务编写一个调用失败时的处理逻辑,称为 fallback。当调用出现故障(比如无线程可用)时,按照失败处理逻辑执行业务并返回,而不是直接抛出异常。
服务熔断
由断路器统计请求的异常比例或慢调用比例,如果超出阈值则会熔断该业务,拦截该接口的请求。熔断期间,所有请求快速失败,全都走 fallback 逻辑。
方案总结
- 请求限流:限制流量在服务可以处理的范围,避免因突发流量而故障
- 线程隔离:控制业务可用的线程数量,将故障隔离在一定范围
- 服务熔断:将异常比例过高的接口断开,拒绝所有请求,直接走 fallback
- 失败处理:定义 fallback 逻辑,让业务失败时不再抛出异常,而是返回默认数据或友好提示
服务保护技术对比
Sentinel 与 Hystrix 是两种主流的服务保护组件,对比如下:
| Sentinel | Hystrix | |
|---|---|---|
| 线程隔离 | 信号量隔离 | 线程池隔离/信号量隔离 |
| 熔断策略 | 基于慢调用比例或异常比例 | 基于异常比率 |
| 限流 | 基于 QPS,支持流量整形 | 有限的支持 |
| Fallback | 支持 | 支持 |
| 控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
| 配置方式 | 基于控制台,重启后失效 | 基于注解或配置文件,永久生效 |
初识 Sentinel
Sentinel 是阿里巴巴开源的一款微服务流量控制组件,官网地址:https://sentinelguard.io/zh-cn/index.html 。它由核心库(Sentinel 客户端)和控制台两部分组成,微服务引入客户端后即可被控制台统一管理。

簇点链路
簇点链路就是单机调用链路,是一次请求进入服务后经过的每一个被 Sentinel 监控的资源链。默认 Sentinel 会监控 SpringMVC 的每一个 Endpoint(http 接口)。限流、熔断等都是针对簇点链路中的资源设置的,资源名默认就是接口的请求路径。

由于 Restful 风格的 API 请求路径一般都相同,会导致簇点资源名称重复。因此需要修改配置,把请求方式 + 请求路径作为簇点资源名称:
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090
http-method-specify: true # 开启请求方式前缀请求限流
在簇点链路后面点击流控按钮,即可对资源做限流配置:


线程隔离
当商品服务出现阻塞或故障时,调用商品服务的购物车服务可能因此被拖慢,甚至资源耗尽。所以必须限制购物车服务中查询商品这个业务的可用线程数,实现线程隔离。
在 Sentinel 控制台中,会出现 Feign 接口的簇点资源,点击后面的流控按钮,即可配置线程隔离。例如限制为 5 个并发线程,如果单线程 QPS 为 2,则 5 线程 QPS 为 10。


Fallback
FeignClient 的 Fallback 有两种配置方式:
- 方式一:FallbackClass,无法对远程调用的异常做处理
- 方式二:FallbackFactory,可以对远程调用的异常做处理,通常都会选择这种
将 FeignClient 作为 Sentinel 的簇点资源,需要开启 feign 对 sentinel 的支持:
feign:
sentinel:
enabled: true编写 Fallback 逻辑
假如有一个 FeignClient:
@FeignClient(value = "userservice")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}步骤一:自定义类,实现 FallbackFactory,编写对某个 FeignClient 的 fallback 逻辑:
@Slf4j
public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
@Override
public UserClient create(Throwable throwable) {
// 创建UserClient接口实现类,实现其中的方法,编写失败降级的处理逻辑
return new UserClient() {
@Override
public User findById(Long id) {
// 记录异常信息,可以返回空或抛出异常
log.error("查询用户失败", throwable);
return null;
}
};
}
}步骤二:将刚刚定义的 UserClientFallbackFactory 注册为一个 Bean:
@Bean
public UserClientFallbackFactory userClientFallback(){
return new UserClientFallbackFactory();
}步骤三:在 UserClient 接口中使用 UserClientFallbackFactory:
@FeignClient(value = "userservice", fallbackFactory = UserClientFallbackFactory.class)
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}服务熔断
熔断是解决雪崩问题的重要手段。思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务,即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。
断路器状态机
断路器有三种状态:
- Closed(关闭):正常放行请求,统计失败比例
- Open(打开):达到失败阈值后打开断路器,所有请求快速失败
- Half-Open(半开):熔断时间结束后尝试放行一次请求,根据结果决定关闭或重新打开断路器
熔断配置
点击控制台中簇点资源后的熔断按钮,即可配置熔断策略:

