一、前言
在微服务架构中,流量突增、下游服务超时、报错雪崩是线上最常见的稳定性问题。Sentinel 作为阿里开源、经过双11大促打磨的流量防护组件,相比 Hystrix 更轻量、功能更强、配置更灵活,是目前国内微服务限流、熔断、降级的主流方案。
本文带来零残缺、可直接运行、可上线的 Sentinel 完整实战案例,包含:
- blockHandler / fallback 精准区分(面试高频)
二、核心核心概念
1. 兜底方法区别
- blockHandler:Sentinel 框架拦截异常,只处理 限流、熔断、系统保护、黑白名单拦截 触发的 BlockException。
- fallback:捕获业务代码所有异常,包含空指针、自定义异常、接口报错等业务异常。
2. 核心能力
- 限流:QPS 限流、并发线程限流、链路限流、热点参数限流
三、环境与依赖
适配版本:SpringBoot 2.7.x + SpringCloud Alibaba 2021.0.1.0(稳定生产版本)
完整 pom.xml 依赖
|
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.15</version>
<relativePath/>
</parent>
<dependencies>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Sentinel 流量防护核心依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2021.0.1.0</version>
</dependency>
<!-- Nacos 配置中心(用于规则持久化,生产必备) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2021.0.1.0</version>
</dependency>
<!-- OpenFeign 远程调用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies> |
四、项目配置文件 application.yml
配置控制台地址、开启服务预热初始化,适配开发、生产环境
|
yaml
server:
port: 8080
spring:
application:
name: sentinel-demo-service
cloud:
sentinel:
# Sentinel 控制台地址
transport:
dashboard: 127.0.0.1:8085
port: 8719
# 关闭懒加载,项目启动直接初始化Sentinel
eager: true
nacos:
config:
server-addr: 127.0.0.1:8848
# 开启feign对sentinel的支持
feign:
sentinel:
enabled: true |
五、启动类
|
java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class SentinelDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SentinelDemoApplication.class, args);
}
} |
六、核心业务代码(限流+熔断+双兜底完整实现)
统一资源名,区分限流熔断兜底、业务异常兜底,代码完全解耦,符合生产规范。
|
java
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
/**
* Sentinel 资源名:createOrder
* blockHandler:限流、熔断、系统保护、授权拦截兜底(Sentinel 框架异常)
* fallback:业务代码异常兜底(代码报错、自定义异常等)
*/
@GetMapping("/order/create/{goodsId}")
@SentinelResource(
value = "createOrder",
blockHandler = "blockHandlerCreateOrder",
fallback = "fallbackCreateOrder"
)
public String createOrder(@PathVariable Long goodsId) {
// 模拟业务异常场景:商品999无库存
if (goodsId == 999) {
throw new RuntimeException("商品库存不足,下单失败");
}
// 模拟慢接口(用于测试慢调用比例熔断)
try {
Thread.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "下单成功,商品ID:" + goodsId;
}
/**
* 限流、熔断兜底方法
* 规则:参数与原方法一致,最后追加 BlockException
*/
public String blockHandlerCreateOrder(Long goodsId, BlockException ex) {
// 区分限流、熔断异常,返回精准提示
if (ex instanceof com.alibaba.csp.sentinel.slots.block.flow.FlowException) {
return "【限流兜底】当前下单人数过多,请稍后重试";
} else if (ex instanceof com.alibaba.csp.sentinel.slots.block.degrade.DegradeException) {
return "【熔断兜底】服务暂时繁忙,已开启熔断保护,请稍后重试";
} else {
return "【服务防护拦截】请求被限制:" + ex.getClass().getSimpleName();
}
}
/**
* 业务异常兜底方法
* 捕获所有业务代码异常
*/
public String fallbackCreateOrder(Long goodsId, Throwable throwable) {
return "【业务异常兜底】商品" + goodsId + "下单失败:" + throwable.getMessage();
}
} |
七、全局异常处理器(生产核心:返回429/503标准状态码)
这是跨服务调用能识别限流/熔断的关键:
1. 限流异常 FlowException → 返回 HTTP 429
2. 熔断异常 DegradeException → 返回 HTTP 503
上游 A 服务可通过状态码精准区分「下游限流」还是「下游熔断」
|
java
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class SentinelGlobalExceptionHandler {
/**
* 下游触发限流:429 Too Many Requests
*/
@ExceptionHandler(FlowException.class)
@ResponseStatus(HttpStatus.TOO_MANY_REQUESTS)
public String handleFlowException(FlowException e) {
return "下游服务触发限流:请求过于频繁,请稍后重试";
}
/**
* 下游触发熔断:503 Service Unavailable
*/
@ExceptionHandler(DegradeException.class)
@ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
public String handleDegradeException(DegradeException e) {
return "下游服务触发熔断:服务暂时不可用,已开启故障隔离";
}
/**
* 通用Sentinel拦截异常
*/
@ExceptionHandler(BlockException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleBlockException(BlockException e) {
return "服务流量拦截:" + e.getMessage();
}
} |
八、OpenFeign 跨服务调用(A服务调用B服务,精准识别限流/熔断)
场景说明
微服务最经典场景:A服务 OpenFeign 调用 B服务
B服务接口被 Sentinel 限流/熔断后,抛出对应异常,经过全局异常处理器返回标准 HTTP 状态码,A服务可精准捕获、区分限流/熔断/普通报错,做差异化降级策略。
1. Feign客户端定义(A服务)
|
java
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "sentinel-demo-service")
public interface BOrderFeignClient {
@GetMapping("/order/create/{goodsId}")
String createOrder(@PathVariable("goodsId") Long goodsId);
} |
2. A服务调用代码(核心:区分429限流 / 503熔断)
|
java
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@Slf4j
@RestController
public class AInvokeBController {
@Resource
private BOrderFeignClient bOrderFeignClient;
@GetMapping("/a/call/b/{goodsId}")
public String aCallB(@PathVariable Long goodsId) {
try {
// A服务远程调用B服务带Sentinel防护的接口
return bOrderFeignClient.createOrder(goodsId);
} catch (FeignException e) {
// 通过HTTP状态码精准区分下游异常类型
int status = e.status();
if (status == 429) {
// 下游B服务触发限流
log.warn("【跨服务降级】B服务接口限流,goodsId:{}", goodsId);
return "系统繁忙,当前访问人数过多,请稍后重试(限流降级)";
} else if (status == 503) {
// 下游B服务触发熔断
log.error("【跨服务熔断】B服务接口熔断,服务暂时不可用,goodsId:{}", goodsId);
return "服务暂时异常,已开启熔断保护,请稍后重试(熔断降级)";
} else {
// 其他业务异常
log.error("【远程调用异常】未知错误:{}", e.getMessage());
return "服务调用异常,请稍后重试";
}
}
}
} |
九、规则配置方式一:代码硬编码(快速测试)
无需控制台,项目启动自动加载限流、熔断规则,适合单元测试、快速验证场景。
|
java
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class SentinelRuleConfig {
// 与接口资源名保持一致
private static final String RESOURCE = "createOrder";
@PostConstruct
public void initSentinelRule() {
// 1. 限流规则:QPS单机阈值2,每秒最多2次请求
List<FlowRule> flowRuleList = new ArrayList<>();
FlowRule flowRule = new FlowRule();
flowRule.setResource(RESOURCE);
// 限流维度:QPS
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 阈值
flowRule.setCount(2);
// 流控效果:快速失败
flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
flowRuleList.add(flowRule);
FlowRuleManager.loadRules(flowRuleList);
// 2. 熔断规则:慢调用比例熔断
List<DegradeRule> degradeRuleList = new ArrayList<>();
DegradeRule degradeRule = new DegradeRule();
degradeRule.setResource(RESOURCE);
// 熔断策略:慢调用RT
degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
// 超过250ms判定为慢调用
degradeRule.setCount(250);
// 慢调用比例超过50%触发熔断
degradeRule.setSlowRatioThreshold(0.5);
// 最小统计请求数
degradeRule.setMinRequestAmount(5);
// 熔断窗口期5秒,期间直接降级
degradeRule.setTimeWindow(5);
degradeRuleList.add(degradeRule);
DegradeRuleManager.loadRules(degradeRuleList);
}
} |
十、规则配置方式二:Sentinel 控制台动态配置(生产推荐)
1. 启动控制台
下载 sentinel-dashboard-1.8.8.jar,执行启动命令:
|
shell
java -jar sentinel-dashboard-1.8.8.jar --server.port=8085 |
访问地址:http://127.0.0.1:8085,默认账号密码:sentinel / sentinel
2. 配置流控规则
3. 配置熔断降级规则
十一、完整测试场景(含跨服务调用)
1. 本地接口限流测试
快速高频访问:http://localhost:8080/order/create/1
每秒请求超过2次,触发限流,返回429提示信息。
2. 业务异常 fallback 测试
访问:http://localhost:8080/order/create/999
代码主动抛出业务异常,触发 fallback 兜底。
3. 跨服务限流识别测试(A调B)
高频访问:http://localhost:8080/a/call/b/1
B服务触发限流后返回429,A服务捕获状态码,执行限流专属降级逻辑。
4. 跨服务熔断识别测试(A调B)
批量并发请求接口,触发B服务慢调用熔断,B返回503,A服务捕获后执行熔断专属降级逻辑,不再重试。
十二、生产核心规范与避坑总结
1. 方法签名硬性规则
- blockHandler 方法:参数必须和原方法一致,末尾必须追加 BlockException
- fallback 方法:参数同原方法,可追加 Throwable 捕获所有异常
2. 跨服务调用核心规范(重点)
- 限流=429:服务正常、流量打满,可短时重试、排队、友好提示
- 熔断=503:服务异常隔离,禁止重试,直接走本地兜底/缓存
- 禁止统一返回200状态码+业务码,会导致上游无法精准区分故障类型,丢失降级策略
Feign 底层逻辑:
只要 HTTP 状态码 不是 200~299,不会把 body 正常返回,直接抛出 FeignException;
FeignException 内部会保存原始 HTTP 状态码,通过 e.status() 获取。
3. 生产建议
- 禁止使用代码硬编码规则,务必使用Nacos 持久化规则,支持动态修改、集群统一生效
- 核心接口配置限流+熔断双重防护,非核心接口可只配置限流
- 统一 429/503 状态码规范,统一上下游降级逻辑
- 开启 Sentinel 监控日志,线上快速定位流量、熔断异常
十三、总结
本文实现了 零缺失、可直接投产 的 Sentinel 限流熔断完整案例,覆盖本地接口防护 + OpenFeign跨服务调用、上下游异常识别、差异化降级全场景,厘清了 blockHandler、fallback、限流熔断跨服务传递的核心原理。
Sentinel 凭借轻量、高可用、功能丰富的特性,完全可以替代 Hystrix,作为微服务稳定性防护的核心组件,是中高级开发必备的技术栈。
所有评论(0)