Sentinel 完整使用流程(Spring Boot 2.x + Nacos 持久化)
一、环境准备
1.1 版本说明
xml
<!-- 核心版本 -->
Spring Boot: 2.7.x
Spring Cloud: 2021.0.x
Spring Cloud Alibaba: 2021.0.5.0
Sentinel: 1.8.6
1.2 启动基础设施
bash
# 1. 启动 Nacos(规则持久化)
docker run -d --name nacos \
-p 8848:8848 \
-e MODE=standalone \
nacos/nacos-server:v2.1.2
# 2. 启动 Sentinel Dashboard(监控控制台)
java -Dserver.port=8080 \
-Dcsp.sentinel.dashboard.server=localhost:8080 \
-Dproject.name=sentinel-dashboard \
-jar sentinel-dashboard-1.8.6.jar
二、项目搭建
2.1 添加依赖
xml
<!-- pom.xml -->
<dependencies>
<!-- Spring Boot -->
<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.5.0</version>
</dependency>
<!-- Sentinel Nacos 数据源 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!-- 可选:热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
2.2 配置文件
yaml
# application.yml
server:
port: 8081
spring:
application:
name: sentinel-demo
cloud:
sentinel:
# 1. 基础配置
enabled: true
eager: true # 立即初始化
# 2. 连接控制台
transport:
dashboard: localhost:8080
port: 8719
# 3. HTTP 请求埋点
filter:
enabled: true
url-patterns: /**
# 4. 规则持久化(Nacos)
datasource:
# 流控规则
flow-rule:
nacos:
server-addr: localhost:8848
namespace: public
data-id: ${spring.application.name}-flow-rules
group-id: SENTINEL_GROUP
data-type: json
rule-type: flow
# 熔断规则
degrade-rule:
nacos:
server-addr: localhost:8848
data-id: ${spring.application.name}-degrade-rules
group-id: SENTINEL_GROUP
rule-type: degrade
# 系统规则
system-rule:
nacos:
server-addr: localhost:8848
data-id: ${spring.application.name}-system-rules
group-id: SENTINEL_GROUP
rule-type: system
# 日志配置
logging:
level:
com.alibaba.csp.sentinel: DEBUG
三、核心代码实现
3.1 主启动类
java
package com.example.sentinel;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SentinelApplication {
public static void main(String[] args) {
SpringApplication.run(SentinelApplication.class, args);
}
}
3.2 业务接口定义
java
package com.example.sentinel.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class DemoController {
/**
* 1. 简单接口 - 流控测试
* http://localhost:8081/api/hello?name=world
*/
@GetMapping("/hello")
@SentinelResource(
value = "hello", // 资源名
blockHandler = "helloBlockHandler", // 流控处理
fallback = "helloFallback" // 异常降级
)
public String hello(@RequestParam String name) {
// 模拟业务逻辑
if ("error".equals(name)) {
throw new RuntimeException("模拟业务异常");
}
return "Hello, " + name;
}
// 流控处理函数(必须是 public,参数+返回值与原方法一致,末尾加 BlockException)
public String helloBlockHandler(String name, BlockException ex) {
return "系统繁忙,请稍后重试(流控触发)";
}
// 异常降级函数(末尾加 Throwable)
public String helloFallback(String name, Throwable throwable) {
return "服务降级:" + throwable.getMessage();
}
/**
* 2. 订单接口 - 复杂示例
* http://localhost:8081/api/order/create?orderId=123&amount=100
*/
@PostMapping("/order/create")
@SentinelResource(
value = "createOrder",
blockHandler = "orderBlockHandler",
blockHandlerClass = OrderBlockHandler.class, // 外部类
fallback = "orderFallback"
)
public ApiResult createOrder(
@RequestParam String orderId,
@RequestParam Double amount) {
// 模拟复杂业务
if (amount > 10000) {
throw new IllegalArgumentException("金额超限");
}
// 模拟数据库操作
try {
Thread.sleep(100); // 模拟耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return ApiResult.success("订单创建成功", orderId);
}
// 内部降级方法
public ApiResult orderFallback(String orderId, Double amount, Throwable t) {
return ApiResult.fail("订单创建失败:" + t.getMessage());
}
/**
* 3. 热点参数限流
* http://localhost:8081/api/product/detail?productId=1001&userId=123
*/
@GetMapping("/product/detail")
@SentinelResource(
value = "getProductDetail",
blockHandler = "hotParamBlockHandler"
)
public ApiResult getProductDetail(
@RequestParam Long productId,
@RequestParam(required = false) Long userId) {
// 参数 0: productId, 参数 1: userId
return ApiResult.success("产品详情", productId);
}
public ApiResult hotParamBlockHandler(Long productId, Long userId,
BlockException ex) {
return ApiResult.fail("热门商品访问受限,请稍后");
}
}
3.3 全局流控处理器
java
package com.example.sentinel.handler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.example.sentinel.vo.ApiResult;
import lombok.extern.slf4j.Slf4j;
/**
* 全局流控处理器
* 统一处理所有没有指定 blockHandler 的资源
*/
@Slf4j
public class GlobalBlockHandler {
// 必须是 public static
public static ApiResult handleException(BlockException ex) {
log.warn("触发全局流控: {}", ex.getClass().getSimpleName());
return ApiResult.fail("系统繁忙,请稍后重试");
}
// 订单模块专用
public static ApiResult orderBlockHandler(String orderId, Double amount,
BlockException ex) {
return ApiResult.fail("订单服务繁忙,请稍后提交");
}
}
3.4 统一返回对象
java
package com.example.sentinel.vo;
import lombok.Data;
@Data
public class ApiResult<T> {
private Integer code;
private String message;
private T data;
private Long timestamp;
public static <T> ApiResult<T> success(String message, T data) {
ApiResult<T> result = new ApiResult<>();
result.setCode(200);
result.setMessage(message);
result.setData(data);
result.setTimestamp(System.currentTimeMillis());
return result;
}
public static <T> ApiResult<T> fail(String message) {
ApiResult<T> result = new ApiResult<>();
result.setCode(500);
result.setMessage(message);
result.setTimestamp(System.currentTimeMillis());
return result;
}
}
四、Nacos 规则配置
4.1 登录 Nacos
访问:http://localhost:8848/nacos
账号/密码:nacos/nacos
4.2 创建流控规则
Data ID: sentinel-demo-flow-rules
Group: SENTINEL_GROUP
格式: JSON
json
[
{
"resource": "hello",
"grade": 1,
"count": 5,
"limitApp": "default",
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
},
{
"resource": "createOrder",
"grade": 1,
"count": 10,
"limitApp": "default",
"strategy": 0,
"controlBehavior": 2,
"maxQueueingTimeMs": 2000,
"clusterMode": false
},
{
"resource": "getProductDetail",
"grade": 1,
"count": 20,
"limitApp": "default",
"strategy": 0,
"controlBehavior": 1,
"warmUpPeriodSec": 10,
"clusterMode": false
}
]
4.3 创建熔断规则
Data ID: sentinel-demo-degrade-rules
Group: SENTINEL_GROUP
json
[
{
"resource": "createOrder",
"grade": 0,
"count": 500,
"timeWindow": 10,
"minRequestAmount": 5,
"slowRatioThreshold": 0.5,
"statIntervalMs": 10000
},
{
"resource": "hello",
"grade": 2,
"count": 5,
"timeWindow": 5,
"minRequestAmount": 5,
"statIntervalMs": 10000
}
]
4.4 创建系统保护规则
Data ID: sentinel-demo-system-rules
Group: SENTINEL_GROUP
json
[
{
"highestSystemLoad": 3.0,
"avgRt": 200,
"maxThread": 50,
"qps": 100,
"highestCpuUsage": 0.8
}
]
五、Sentinel Dashboard 使用
5.1 访问控制台
默认账号:sentinel/sentinel
5.2 查看实时监控
- 首页:查看应用列表,机器健康状态
- 簇点链路 :
- 查看所有受保护的资源
- 实时 QPS、响应时间
- 配置规则
- 实时监控:图表展示
5.3 手动配置规则
除了 Nacos 持久化,可以在 Dashboard 临时配置:
- 点击 簇点链路
- 找到资源(如
/api/hello) - 点击 流控
- 设置 QPS=2
- 立即生效(重启应用会丢失,需要手动推送到 Nacos)
六、测试验证
6.1 启动应用
bash
mvn spring-boot:run
查看启动日志,确认 Sentinel 初始化成功:
[Sentinel Starter] DataSource NacosDataSource started
Loading flow rules from Nacos: [{"resource":"hello","count":5,...}]
6.2 测试流控
bash
# 使用 ab 测试
ab -n 100 -c 10 "http://localhost:8081/api/hello?name=world"
# 观察结果
# 正常返回:Hello, world
# 触发流控:系统繁忙,请稍后重试
6.3 测试熔断
java
// 快速触发异常
for (int i = 0; i < 10; i++) {
// 访问会抛出异常的接口
http://localhost:8081/api/hello?name=error
}
// 触发熔断后,即使正常请求也会被拒绝
七、高级功能配置
7.1 动态规则扩展
java
package com.example.sentinel.config;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
* 动态规则管理(管理后台用)
*/
@RestController
@RequestMapping("/sentinel/rule")
public class DynamicRuleController {
/**
* 动态添加流控规则
*/
@PostMapping("/flow/add")
public String addFlowRule(
@RequestParam String resource,
@RequestParam int count) {
List<FlowRule> rules = new ArrayList<>(FlowRuleManager.getRules());
FlowRule rule = new FlowRule();
rule.setResource(resource);
rule.setGrade(1); // QPS
rule.setCount(count);
rule.setLimitApp("default");
rules.add(rule);
FlowRuleManager.loadRules(rules);
return "规则添加成功:" + resource;
}
/**
* 动态移除规则
*/
@PostMapping("/flow/remove")
public String removeFlowRule(@RequestParam String resource) {
List<FlowRule> rules = FlowRuleManager.getRules();
rules.removeIf(rule -> rule.getResource().equals(resource));
FlowRuleManager.loadRules(rules);
return "规则移除成功:" + resource;
}
}
7.2 全局统一异常处理
java
package com.example.sentinel.config;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.example.sentinel.vo.ApiResult;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理器
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
// 处理 Sentinel 流控异常
@ExceptionHandler(BlockException.class)
public ApiResult<Void> handleBlockException(BlockException e) {
return ApiResult.fail("系统限流中,请稍后重试");
}
// 处理业务异常
@ExceptionHandler(RuntimeException.class)
public ApiResult<Void> handleRuntimeException(RuntimeException e) {
return ApiResult.fail("业务异常:" + e.getMessage());
}
}
八、生产环境建议
8.1 配置建议
yaml
spring:
cloud:
sentinel:
# 生产配置
log:
dir: /var/log/sentinel/${spring.application.name}
switch-pid: true
# 集群限流(可选)
flow:
enable: true
cluster:
enabled: true
fallback-to-local-when-fail: true
# 网关支持
scg:
enabled: true
8.2 监控告警
java
// 自定义指标收集
@Component
public class SentinelMetricsCollector {
@PostConstruct
public void init() {
// 注册自定义指标
MetricsExtension.register();
}
}
8.3 健康检查端点
yaml
# application.yml
management:
endpoints:
web:
exposure:
include: health,metrics,sentinel
endpoint:
health:
show-details: always
访问:http://localhost:8081/actuator/health
查看 Sentinel 健康状况。
九、常见问题排查
9.1 规则不生效
- 检查 Nacos 配置的 Data ID、Group 是否正确
- 查看应用日志,确认加载了规则
- 确认
@SentinelResource的 value 与 resource 一致
9.2 控制台看不到应用
- 确认
spring.cloud.sentinel.transport.dashboard配置正确 - 检查防火墙,端口 8719 是否开放
- 查看应用日志,确认成功连接 Dashboard
9.3 规则不持久化
- 确认在 Dashboard 修改规则后,点击了"推送到 Nacos"
- 检查 Nacos 配置是否被修改
- 重启应用,看是否加载了最新规则
十、快速验证脚本
bash
#!/bin/bash
# sentinel-test.sh
# 1. 启动服务
echo "启动服务..."
java -jar sentinel-demo.jar &
# 2. 等待启动
sleep 30
# 3. 测试正常请求
echo "测试正常请求..."
for i in {1..3}
do
curl "http://localhost:8081/api/hello?name=test$i"
echo ""
done
# 4. 测试流控
echo "测试流控(触发限流)..."
for i in {1..10}
do
curl "http://localhost:8081/api/hello?name=limit$i"
echo ""
sleep 0.1
done
# 5. 检查控制台
echo "请在浏览器访问:"
echo "Sentinel Dashboard: http://localhost:8080"
echo "Nacos: http://localhost:8848"
运行流程总结:
- ✅ 启动 Nacos、Sentinel Dashboard
- ✅ 创建 Spring Boot 项目,添加依赖
- ✅ 配置 application.yml,连接 Nacos
- ✅ 在业务方法上加
@SentinelResource - ✅ 在 Nacos 中创建规则 JSON
- ✅ 启动应用,验证规则加载
- ✅ 通过 Dashboard 监控
- ✅ 测试流控、熔断效果
这套流程覆盖了 Sentinel 从入门到生产的所有关键环节,按步骤操作即可搭建完整的流量防护系统。
Sentinel 与 OpenFeign 的整合逻辑比较特殊:它不直接使用 @SentinelResource 注解(如 blockHandlerClass),而是通过 fallback 机制实现熔断降级。你需要区分"Feign 客户端"与"普通方法"在配置上的本质区别。
一、基础整合:开启 Sentinel 支持
- 依赖与配置
确保你的 spring-cloud-alibaba 版本与 Sentinel 兼容(如 2022.x 或 2023.x)。
application.yml
feign:
sentinel:
enabled: true # 关键:必须开启
com.alibaba.cloud spring-cloud-starter-alibaba-sentinel org.springframework.cloud spring-cloud-starter-openfeign
- 定义 Feign 客户端与 Fallback
注意:Feign 客户端接口上使用的是 @FeignClient 的 fallback 或 fallbackFactory 属性,不是 @SentinelResource。
// 1. Feign 客户端接口
@FeignClient(
name = "user-service",
path = "/api/user",
fallback = UserServiceFallback.class // 指定降级类
)
public interface UserServiceFeignClient {
@GetMapping("/{id}")
ApiResult<User> getUserById(@PathVariable Long id);
@PostMapping
ApiResult<Void> createUser(@RequestBody UserDTO user);
}
- 实现 Fallback 降级类
降级类必须实现 Feign 接口,且必须是 Spring Bean。
// 2. Fallback 实现(处理限流、熔断、服务不可用)
@Component
public class UserServiceFallback implements UserServiceFeignClient {
@Override
public ApiResult<User> getUserById(Long id) {
// 当触发 Sentinel 流控或服务调用失败时,进入此方法
return ApiResult.fail("用户服务繁忙,请稍后重试");
}
@Override
public ApiResult<Void> createUser(UserDTO user) {
return ApiResult.fail("用户服务不可用");
}
}
二、进阶:使用 FallbackFactory(推荐)
如果你需要获取异常信息(如区分是限流 BlockException 还是网络超时),请使用 fallbackFactory。
// 1. 修改 @FeignClient 注解
@FeignClient(
name = "user-service",
fallbackFactory = UserServiceFallbackFactory.class // 改为工厂
)
public interface UserServiceFeignClient {
// ... 方法不变
}
// 2. FallbackFactory 实现
@Component
@Slf4j
public class UserServiceFallbackFactory implements FallbackFactory {
@Override
public UserServiceFeignClient create(Throwable cause) {
// cause 就是异常原因(BlockException、FeignException等)
return new UserServiceFeignClient() {
@Override
public ApiResult<User> getUserById(Long id) {
log.error("调用 getUserById 失败,原因:", cause);
// 判断是否为 Sentinel 限流
if (cause instanceof BlockException) {
return ApiResult.fail("请求过于频繁,请稍后重试(限流)");
} else {
return ApiResult.fail("用户服务异常:" + cause.getMessage());
}
}
@Override
public ApiResult<Void> createUser(UserDTO user) {
// ... 类似处理
return ApiResult.fail("创建失败");
}
};
}
}
三、Sentinel 流控规则配置
Feign 整合后,Sentinel 会自动将每个 Feign 客户端方法注册为一个资源,资源名格式通常为:
- 默认规则:GET:http://user-service/api/user/{id}
• 自定义:可通过 @FeignClient 的 qualifier 或配置指定
在 Dashboard 中配置
- 访问 Sentinel 控制台(如 localhost:8080)。
- 找到你的服务 -> 簇点链路。
- 找到对应的 Feign 接口 URL 资源。
- 设置 流控规则(QPS/线程数)或 降级规则(异常比例/响应时间)。
规则生效逻辑
• 触发流控:当 QPS 超过阈值,Sentinel 会抛出 BlockException。
- Feign 捕获:Feign 捕获到 BlockException 或其它异常后,不再重试,直接调用你定义的 fallback 方法。
- 返回降级结果:用户收到友好的"服务繁忙"提示,而不是 500 错误。
四、常见误区与避坑
❌ 误区 1:在 Feign 接口上使用 @SentinelResource
// ❌ 错误写法:Feign 不支持在此处使用 @SentinelResource
@FeignClient(name = "user-service", fallback = ...)
public interface UserServiceFeignClient {
@SentinelResource(blockHandlerClass = ...) // 无效!
@GetMapping("/{id}")
ApiResult<User> getUserById(@PathVariable Long id);
}
结论:@SentinelResource 的 blockHandlerClass 仅用于 Spring MVC Controller 或 普通 Service 方法,对 Feign 客户端无效。
❌ 误区 2:Fallback 类未被 Spring 扫描
// ❌ 错误:Fallback 类不在启动类扫描路径下
@Component
public class UserServiceFallback implements UserServiceFeignClient { ... }
// 启动类扫描包范围不对,导致 Fallback 无法注入,启动报错:
// No fallback instance found for feign client user-service
解决:确保 Fallback 类在 @SpringBootApplication 主类或其子包下,或显式指定扫描路径:
@EnableFeignClients(basePackages = "com.yourcompany.feign")
✅ 正确对比:Feign vs RestTemplate
组件 限流/降级配置方式 备注
OpenFeign @FeignClient(fallback = ...) 不支持 @SentinelResource
RestTemplate @SentinelRestTemplate 支持 blockHandlerClass
// RestTemplate 才用 blockHandlerClass
@Bean
@LoadBalanced
@SentinelRestTemplate(
blockHandler = "handleBlock",
blockHandlerClass = SentinelBlockHandler.class
)
public RestTemplate restTemplate() {
return new RestTemplate();
}
五、最佳实践总结
-
必开开关:feign.sentinel.enabled=true。
-
推荐使用 FallbackFactory:便于根据异常类型(限流 vs 宕机)做不同降级处理。
-
资源名管理:在 Sentinel 控制台中,Feign 资源名通常为 HTTP 方法 + URL,建议保持接口路径清晰。
-
区分场景:
◦ Feign 调用:用 @FeignClient(fallbackFactory = ...)
◦ 内部方法:用 @SentinelResource(blockHandlerClass = ...)
如果你需要为 Feign 调用单独配置复杂的流控逻辑(如基于参数限流),建议在 Feign 调用外层的 Controller 或 Service 方法上添加 @SentinelResource,而不是直接在 Feign 接口上操作。