Sentinel

一、Sentinel 介绍

1. 为什么使用 Sentinel?应用场景?

在微服务架构中,服务之间存在着复杂的调用关系。当一个服务发生故障时,如果没有适当的保护措施,可能会导致连锁反应,引发服务雪崩效应

典型场景

  • 上游服务(调用方) → 当前服务(被调用方) → 下游服务(依赖方)

  • 当前服务既要防止被上游的流量压垮,也要避免被下游的故障拖垮

2. Sentinel 是什么?

Sentinel (/ˈsentɪnl/)是阿里巴巴开源的一款以流量为切入点的分布式系统服务保护框架。它就像是服务的"哨兵",时刻监控和保护着系统的稳定运行。

核心功能

  1. 流量控制​ - 保护自己不被上游压垮

    • QPS 控制:限制每秒请求次数

    • 线程数控制:限制并发线程数

    • 原理:快速失败、预热、排队等待等策略

  2. 熔断降级​ - 保护自己不被下游拖垮

    • 熔断:当检测到异常达到阈值时,暂时切断对故障服务的调用

    • 降级:返回预设的默认值,保证主业务流程的可用性

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 作为一款强大的流量控制组件,在微服务架构中扮演着"守护者"的角色。通过合理的配置和使用,可以:

  1. 防止服务雪崩:通过熔断降级机制

  2. 保障系统稳定:通过流量控制机制

  3. 提升用户体验:通过优雅降级策略

  4. 便于运维管理:通过规则持久化和动态配置

相关推荐
城管不管1 小时前
mysql与pgsql
数据库·mysql·pgsql
当战神遇到编程2 小时前
MySQL 函数与分组篇(聚合函数 + GROUP BY + 常用函数)
数据库·mysql
南境十里·墨染春水2 小时前
C++流类库 文件流操作
开发语言·c++
u0109147602 小时前
C#怎么使用Span和Memory C#如何用Span优化内存操作减少GC压力提升性能【进阶】
jvm·数据库·python
咸鱼翻身小阿橙2 小时前
Qt页面小项目
开发语言·qt·计算机视觉
GoodStudyAndDayDayUp2 小时前
优化java加权方法
java·优化java加权方法
阿丰资源2 小时前
基于SpringBoot+MySQL的时装购物系统(附源码)
java·spring boot·mysql
m0_716430072 小时前
CSS项目开发如何提速_应用BEM规范建立可复用的样式库
jvm·数据库·python
gjc5922 小时前
MySQL运维避坑:你的MySQL总是关机慢、启动卡?
运维·数据库·mysql