一、Sentinel 介绍
1. 为什么使用 Sentinel?应用场景?
在微服务架构中,服务之间存在着复杂的调用关系。当一个服务发生故障时,如果没有适当的保护措施,可能会导致连锁反应,引发服务雪崩效应。
典型场景:
-
上游服务(调用方) → 当前服务(被调用方) → 下游服务(依赖方)
-
当前服务既要防止被上游的流量压垮,也要避免被下游的故障拖垮
2. Sentinel 是什么?
Sentinel (/ˈsentɪnl/)是阿里巴巴开源的一款以流量为切入点的分布式系统服务保护框架。它就像是服务的"哨兵",时刻监控和保护着系统的稳定运行。
核心功能:
-
流量控制 - 保护自己不被上游压垮
-
QPS 控制:限制每秒请求次数
-
线程数控制:限制并发线程数
-
原理:快速失败、预热、排队等待等策略
-
-
熔断降级 - 保护自己不被下游拖垮
-
熔断:当检测到异常达到阈值时,暂时切断对故障服务的调用
-
降级:返回预设的默认值,保证主业务流程的可用性
-
3. 快速开始
添加依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
二、Sentinel 核心规则详解
1. 流控规则(Flow Control)
1.1 基本流控规则
// 应用到具体接口
@GetMapping("/test")
@SentinelResource(value = "testResource", blockHandler = "blockHandler")
public String test() {
return "Hello Sentinel";
}
// 流控降级处理
public String blockHandler(BlockException ex) {
return "请求过于频繁,请稍后再试";
}
控制台配置:
-
资源名:testResource
-
阈值类型:QPS/线程数
-
单机阈值:2
-
流控模式:直接/关联/链路
-
流控效果:快速失败/Warm Up/排队等待
1.2 热点参数流控
针对特定参数进行精细控制,比如:
-
商品ID为123的商品,5秒内只能访问2次
-
用户ID为456的用户,限制其访问频率
1.3 授权规则
基于调用来源进行控制:
# 只允许来自app的调用
if (origin == "app") {
// 放行
} else {
// 拦截
}
1.4 系统规则
保护整个应用,防止系统过载:
-
LOAD:系统负载
-
RT:平均响应时间
-
线程数
-
入口QPS
-
CPU使用率
2. 降级规则(Degrade Rule)
2.1 异常比例降级
降级策略:
统计时长:5000ms
最小请求数:5
比例阈值:0.1
熔断时长:5000ms
触发条件:在5秒内,至少有5个请求,其中异常比例超过10%,则触发熔断5秒。
2.2 异常数降级
降级策略:
统计时长:5000ms
最小请求数:5
异常数阈值:1
熔断时长:5000ms
触发条件:5秒内至少有5个请求,异常数超过1个就熔断。
2.3 慢调用比例降级
降级策略:
统计时长:5000ms
最小请求数:5
最大RT:1ms
比例阈值:0.1
熔断时长:5000ms
触发条件:响应时间超过1ms的比例达到10%,触发熔断。
三、Sentinel 整合 Feign
1. 为什么要整合?
在微服务架构中,服务间调用通常通过 Feign 实现。为了在服务调用链路上实现熔断降级,需要在 Feign 客户端集成 Sentinel。
2. 整合步骤
步骤1:开启 Feign 对 Sentinel 的支持
# application.yml
feign:
sentinel:
enabled: true # 开启 Sentinel 支持
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
步骤2:创建降级逻辑工厂
@Component
public class UserFeignFallbackFactory implements FallbackFactory<UserFeign> {
@Override
public UserFeign create(Throwable cause) {
return new UserFeign() {
@Override
public User getUserById(Integer id) {
// 记录日志
log.error("调用用户服务失败,用户ID:{},异常:{}", id, cause.getMessage());
// 返回降级数据
return User.builder()
.id(id)
.name("系统繁忙,请稍后再试")
.age(0)
.build();
}
@Override
public List<User> getUsers() {
return Collections.emptyList();
}
};
}
}
步骤3:在 Feign 客户端指定降级工厂
@FeignClient(
value = "user-service",
fallbackFactory = UserFeignFallbackFactory.class,
configuration = FeignConfig.class
)
public interface UserFeign {
@GetMapping("/user/{id}")
User getUserById(@PathVariable("id") Integer id);
@GetMapping("/users")
List<User> getUsers();
}
步骤4:自定义 Feign 配置
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
@Bean
public ErrorDecoder errorDecoder() {
return new CustomErrorDecoder();
}
}
四、Sentinel 全局异常处理
统一异常处理类
@Component
public class GlobalBlockExceptionHandler implements BlockExceptionHandler {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
BlockException e) throws Exception {
response.setContentType("application/json;charset=utf-8");
response.setStatus(429); // Too Many Requests
Result<?> result = null;
if (e instanceof FlowException) {
// 流控异常
result = Result.error(429, "请求过于频繁,请稍后再试");
} else if (e instanceof DegradeException) {
// 降级异常
result = Result.error(503, "服务暂时不可用,请稍后再试");
} else if (e instanceof AuthorityException) {
// 授权异常
result = Result.error(403, "无权限访问该资源");
} else if (e instanceof SystemBlockException) {
// 系统保护异常
result = Result.error(503, "系统保护中,请稍后再试");
} else if (e instanceof ParamFlowException) {
// 热点参数限流
result = Result.error(429, "热点参数限流");
} else {
// 其他异常
result = Result.error(500, "系统繁忙");
}
// 记录日志
log.warn("Sentinel Block Exception: {}", e.getClass().getSimpleName());
// 返回 JSON 响应
response.getWriter().write(objectMapper.writeValueAsString(result));
}
}
// 统一响应体
@Data
@AllArgsConstructor
@NoArgsConstructor
class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
return new Result<>(200, "success", data);
}
public static <T> Result<T> error(Integer code, String message) {
return new Result<>(code, message, null);
}
}
五、Sentinel 规则持久化
方案1:Sentinel Dashboard 推送规则到 Nacos
步骤1:修改 Sentinel Dashboard
# application.properties
nacos.address=192.168.61.132:8848
nacos.namespace=sentinel
nacos.groupId=SENTINEL_GROUP
nacos.username=nacos
nacos.password=nacos
步骤2:打包并运行
# 使用已集成了 Nacos 的 Sentinel Dashboard
java -jar sentinel-dashboard-nacos-1.8.1.jar
方案2:微服务从 Nacos 拉取规则
步骤1:添加依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
步骤2:配置 Nacos 数据源
spring:
application:
name: order-service
cloud:
sentinel:
# Sentinel 控制台地址
transport:
dashboard: localhost:8080
port: 8719
# 规则持久化到 Nacos
datasource:
# 流控规则
flow:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
namespace: sentinel
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-flow-rules
rule-type: flow
data-type: json
# 降级规则
degrade:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
namespace: sentinel
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-degrade-rules
rule-type: degrade
# 系统规则
system:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
namespace: sentinel
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-system-rules
rule-type: system
# 授权规则
authority:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
namespace: sentinel
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-authority-rules
rule-type: authority
# 热点参数规则
param-flow:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
namespace: sentinel
groupId: SENTINEL_GROUP
dataId: ${spring.application.name}-param-flow-rules
rule-type: param-flow
步骤3:Nacos 规则配置示例
// order-service-flow-rules
[
{
"resource": "getOrderById",
"limitApp": "default",
"grade": 1,
"count": 10,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
// order-service-degrade-rules
[
{
"resource": "getOrderById",
"grade": 0,
"count": 0.1,
"timeWindow": 5,
"minRequestAmount": 5,
"statIntervalMs": 1000
}
]
六、最佳实践
1. 生产环境建议
-
开启规则持久化,避免重启丢失
-
合理设置阈值,避免误拦截正常请求
-
结合监控系统,实时观察系统状态
-
建立告警机制,及时发现异常
2. 性能优化
-
合理设置统计时长和最小请求数
-
根据业务高峰低谷动态调整规则
-
使用集群流控应对大流量场景
3. 监控与运维
@RestController
@RequestMapping("/sentinel")
public class SentinelMonitorController {
@Autowired
private SentinelProperties sentinelProperties;
/**
* 获取当前应用的 Sentinel 规则
*/
@GetMapping("/rules")
public Map<String, List<?>> getRules() {
Map<String, List<?>> rules = new HashMap<>();
// 流控规则
rules.put("flowRules", FlowRuleManager.getRules());
// 降级规则
rules.put("degradeRules", DegradeRuleManager.getRules());
// 系统规则
rules.put("systemRules", SystemRuleManager.getRules());
// 授权规则
rules.put("authorityRules", AuthorityRuleManager.getRules());
// 热点参数规则
rules.put("paramFlowRules", ParamFlowRuleManager.getRules());
return rules;
}
/**
* 获取资源统计信息
*/
@GetMapping("/metrics")
public Map<String, Node> getMetrics() {
return ClusterNodeBuilder.getClusterNodeMap();
}
}
总结
Sentinel 作为一款强大的流量控制组件,在微服务架构中扮演着"守护者"的角色。通过合理的配置和使用,可以:
-
防止服务雪崩:通过熔断降级机制
-
保障系统稳定:通过流量控制机制
-
提升用户体验:通过优雅降级策略
-
便于运维管理:通过规则持久化和动态配置