Springcloud Alibaba相关组件系列文章如下:
SpringCloud Alibaba实战之Nacos-CSDN博客
SpringCloud Alibaba实战之OpenFeign-CSDN博客
SpringCloud Alibaba实战之Sentinel-CSDN博客
SpringCloud Alibaba实战之Gateway-CSDN博客
SpringCloud Alibaba实战之Seata-CSDN博客
sentinel
官网:https://github.com/alibaba/Sentinel/wiki
随着微服务的流行,服务和服务之间的稳定性变得越来越重要,SpringCloud Alibaba Sentinel以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。



1、sentinel简单使用
1. 下载sentinel-dashboard的jar包,官网地址:https://github.com/alibaba/Sentinel。

2. 使用java -jar sentinel-dashboard-xxx.jar命令启动sentinel-dashboard,访问8080端口登录sentinel,用户名和密码都是sentinel。


登录sentinel之后,sentinel-dashboard界面是没有任何资源的,必须启动微服务并调用接口之后,在sentinel控制台才能看到被调用接口的资源信息。可以在application.yml配置文件中通过spring.cloud.sentinel.eager: true 属性设置饥饿加载,服务启动就连接sentinel,默认是第一次调用服务才会连接sentinel。

3. 在services模块的pom.xml文件中引入sentinel的依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud-demo</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>services</artifactId>
<packaging>pom</packaging>
<modules>
<module>service-order</module>
<module>service-product</module>
</modules>
<dependencies>
<!-- 引入model模块 -->
<dependency>
<groupId>org.example</groupId>
<artifactId>model</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 启动Springboot服务,内嵌Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 服务注册与发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- 负载均衡远程调用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- 服务调用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 服务保护(限流、熔断降级) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
4. 在application.yml文件中添加sentinel的配置
sentinel:
transport:
# 配置sentinel地址,连接sentinel
dashboard: localhost:8080
eager: true # 服务启动就连接sentinel,默认是第一次调用服务才会连接sentinel
service-order服务的application.yml文件内容如下:
server:
port: 8000
spring:
profiles:
active: dev # 通过修改spring.profiles.active属性的值来选择不同环境的配置
include: feign # 引入application-feign.yml文件
application:
name: service-order
cloud:
nacos:
# 配置nacos地址,将微服务注册到nacos上
server-addr: 127.0.0.1:8848
config:
import-check:
enabled: false
# 如果没有配置spring.profiles.active,则默认使用public命名空间
namespace: ${spring.profiles.active:public}
sentinel:
transport:
# 配置sentinel地址,连接sentinel
dashboard: localhost:8080
eager: true # 服务启动就连接sentinel,默认是第一次调用服务才会连接sentinel
logging:
level:
# 给com.springcloud.demo.order.feign包下的类设置日志级别为debug
com.springcloud.demo.order.feign: debug
---
spring:
config:
import:
- nacos:common.properties?group=service-order
- nacos:database.properties?group=service-order
activate:
on-profile: dev
---
spring:
config:
import:
- nacos:common.properties?group=service-order
activate:
on-profile: test
---
spring:
config:
import:
- nacos:database.properties?group=service-order
activate:
on-profile: product
service-product服务的application.yml文件内容如下:
server:
port: 9000
spring:
application:
name: service-product
cloud:
nacos:
# 配置nacos地址,将微服务注册到nacos上
server-addr: 127.0.0.1:8848
config:
# 如果引入了Nacos配置中心依赖,需要导入Nacos的配置,或禁用Nacos配置检查,否则服务启动失败
import-check:
enabled: false
sentinel:
transport:
# 配置sentinel地址,连接sentinel
dashboard: localhost:8080
eager: true # 服务启动就连接sentinel,默认是第一次调用服务才会连接sentinel
5. 在OrderServiceImpl类中通过@SentinelResource注解定义一个sentinel资源
package com.springcloud.demo.order.service.impl;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.springcloud.demo.order.bean.Order;
import com.springcloud.demo.order.config.OrderProperties;
import com.springcloud.demo.order.feign.ProductFeignClient;
import com.springcloud.demo.order.service.OrderService;
import com.springcloud.demo.product.bean.Product;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Collections;
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private ProductFeignClient productFeignClient;
@Autowired
private OrderProperties orderProperties;
/**
* 从Nacos配置中心获取配置信息
*
* @return 配置信息
*/
@Override
public String getConfig() {
String s = "order.timeout=" + orderProperties.getTimeout()
+ ", order.auto-confirm=" + orderProperties.getAutoConfirm()
+ ", order.db-url=" + orderProperties.getDbUrl();
log.info("The nacos config is: {}.", s);
return s;
}
@SentinelResource(value = "createOrder") // 定义一个sentinel资源
@Override
public Order createOrder(Long userId, Long productId) {
Product product = productFeignClient.getProductById(productId);
Order order = new Order();
order.setId(1L);
order.setTotalAmount(product.getPrice().multiply(new BigDecimal(product.getNum())));
order.setUserId(userId);
order.setNickName("zhangsan");
order.setAddress("上海");
order.setProductList(Collections.singletonList(product));
return order;
}
}
6. 启动service-order和service-product服务,先调用service-order服务接口,再访问sentinel-dashboard界面。

如下图所示,可以对每一个资源配置流控、熔断、热点和授权等操作,这些操作都可以被称为规则。

例如对于/order/create资源配置流控规则QPS=1,也就是/order/create接口每秒最多支持1个请求,超过流控规则就会触发sentinel限制,抛出BlockException异常。



2、sentinel资源异常处理

Sentinel资源可以简单分成web接口类型的资源、@SentinelResource注解标记的资源、OpenFeign调用类型的资源等,对于不同类型的sentinel资源配置流控、熔断、热点、授权等规则之后,如果请求资源时违反了规则限制,则会抛出BlockException异常,如果不对该异常处理,直接抛给前端,对前端不太友好,所以针对这几种类型的资源抛出BlockException异常时分别提供不同的处理方式。

1、web接口sentinel资源的异常处理
1. 为了给前端返回统一的数据格式,如下所示:
{
"code": 200,
"msg": "success",
"data": {}
}
在model模块创建Result实体类:
package com.springcloud.demo.common;
import lombok.Data;
@Data
public class Result {
private Integer code;
private String msg;
private Object data;
public static Result ok() {
Result result = new Result();
result.setCode(200);
result.setMsg("success");
return result;
}
public static Result ok(Object data) {
Result result = ok();
result.setData(data);
return result;
}
public static Result error() {
Result result = new Result();
result.setCode(500);
return result;
}
public static Result error(Integer code, String msg) {
Result result = new Result();
result.setCode(code);
result.setMsg(msg);
return result;
}
}
2. 在service-order服务中自定义异常处理器,实现BlockExceptionHandler接口
package com.springcloud.demo.order.exception;
import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.springcloud.demo.common.Result;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import java.io.PrintWriter;
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
private final ObjectMapper objectMapper = new ObjectMapper();
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
String resourceName, BlockException e) throws Exception {
response.setContentType("application/json;charset=utf-8");
try (PrintWriter writer = response.getWriter()) {
String cause = e.getMessage() == null ? e.toString() : e.getMessage();
String msg = resourceName + " 被sentinel限制了,原因:" + cause;
Result error = Result.error(500, msg);
writer.write(objectMapper.writeValueAsString(error));
writer.flush();
}
}
}
3. 通过postman连续快速调用service-order服务的/order/create接口。

2、@SentinelResource标记的资源异常处理
1. 使用@SentinelResource注解标记某个业务方法作为资源,并设置blockHandler属性。
package com.springcloud.demo.order.service.impl;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.springcloud.demo.order.bean.Order;
import com.springcloud.demo.order.config.OrderProperties;
import com.springcloud.demo.order.feign.ProductFeignClient;
import com.springcloud.demo.order.service.OrderService;
import com.springcloud.demo.product.bean.Product;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Collections;
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private ProductFeignClient productFeignClient;
@Autowired
private OrderProperties orderProperties;
/**
* 从Nacos配置中心获取配置信息
*
* @return 配置信息
*/
@Override
public String getConfig() {
String s = "order.timeout=" + orderProperties.getTimeout()
+ ", order.auto-confirm=" + orderProperties.getAutoConfirm()
+ ", order.db-url=" + orderProperties.getDbUrl();
log.info("The nacos config is: {}.", s);
return s;
}
/**
* 使用@SentinelResource注解定义一个sentinel资源,用于标记需要受保护的资源。
* value:资源名称,在控制台配置规则的依据,该属性是必须的。
* entryType:流量方向:IN(入口)/OUT(出口)。
* blockHandler:处理BlockException异常的降级方法名。
* fallback:业务异常时的降级方法名。
* defaultFallback:默认降级方法名(无参或单Throwable参数)。
* exceptionsToIgnore:忽略的异常类型(不触发fallback)。
* 被@SentinelResource注解标记为资源的业务方法,如果违反资源的规则,会抛出BlockException异常,
* 则按顺序依次使用blockHandler、fallback或defaultFallback指定的方法进行兜底回调,
* 若这三个属性都没有指定兜底回调方法,则抛出BlockException异常信息。
*
* @param userId 用户id
* @param productId 商品id
* @return 订单信息
*/
@SentinelResource(value = "createOrder", blockHandler = "createOrderFallback")
@Override
public Order createOrder(Long userId, Long productId) {
Product product = productFeignClient.getProductById(productId);
Order order = new Order();
order.setId(1L);
order.setTotalAmount(product.getPrice().multiply(new BigDecimal(product.getNum())));
order.setUserId(userId);
order.setNickName("zhangsan");
order.setAddress("上海");
order.setProductList(Collections.singletonList(product));
return order;
}
// 方法参数需要与原方法相同,可以在最后添加BlockException或Throwable
// 若原方法上@SentinelResource注解使用的是blockHandler,则该方法添加BlockException,
// 若原方法上@SentinelResource注解使用的是fallback,则该方法添加Throwable
public Order createOrderFallback(Long userId, Long productId, BlockException e) {
Order order = new Order();
order.setId(0L);
order.setTotalAmount(new BigDecimal("0"));
order.setUserId(userId);
order.setNickName(e.toString());
return order;
}
}
2. 在sentinel控制台配置对应资源的规则,例如配置"createOrder"资源每秒最多请求1次。

3. 通过postman多次调用service-order服务创建订单的接口,观察触发兜底回调的响应结果。
package com.springcloud.demo.order.controller;
import com.springcloud.demo.common.Result;
import com.springcloud.demo.order.bean.Order;
import com.springcloud.demo.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
/**
* POST请求接收JSON数据时使用简单类型(Integer、String等)不能自动填充数据
* 必须要封装成实体类或者使用Map接收,而且使用Map接收时还要注明泛型
*
* @param map 请求参数
* @return 订单信息
*/
@PostMapping("/create")
public Result createOrder(@RequestBody Map<String, Object> map) {
Long userId = Long.parseLong(String.valueOf(map.get("userId")));
Long productId = Long.parseLong(String.valueOf(map.get("productId")));
Order order = orderService.createOrder(userId, productId);
return Result.ok(order);
}
@GetMapping("/config")
public Result getConfig() {
return Result.ok(orderService.getConfig());
}
}

3、OpenFeign调用类型的sentinel资源异常处理
1. 在application-feign.yml或application.yml文件中开启sentinel的熔断降级功能。
spring:
cloud:
openfeign:
client:
config:
default: # 默认连接超时和读取超时都设置成30秒,对所有feign客户端生效
logger-level: full
connect-timeout: 30000
read-timeout: 30000
service-product: # 被调用的服务名称,@FeignClient注解value属性值
logger-level: full
connect-timeout: 3000
read-timeout: 5000
# request-interceptors:
# - com.springcloud.demo.order.interceptor.XTokenRequestInterceptor
feign:
sentinel:
enabled: true # 开启熔断降级功能
2. 上面在OrderServiceImpl类的createOrder方法中,通过ProductFeignClient接口远程调用service-product服务的getProductById方法获取商品信息,ProductFeignClient接口如下所示。当getProductById方法出现异常时,通过@FeignClient注解的fallback属性指定的兜底回调返回默认的数据。
package com.springcloud.demo.order.feign;
import com.springcloud.demo.order.feign.fallback.ProductFeignClientFallback;
import com.springcloud.demo.product.bean.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @FeignClient是Spring Cloud提供的一个声明式HTTP客户端注解,主要用于简化微服务之间的远程调用。
* 通过简单的接口定义和注解配置,即可实现服务间的HTTP调用,开发者无需手动编写HTTP请求代码。
* 多种参数配置:
* value/name:指定要调用的服务名称
* url:直接指定服务URL,绕过服务发现,使用该方式调用没有在Nacos上注册的第三方服务
* fallback:定义调用失败时的降级处理类
* configuration:自定义Feign客户端配置
*/
@FeignClient(value = "service-product", fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {
/**
* 调用"/product/query/{id}"接口发送请求获取结果
* @GetMapping、@PostMapping、@PutMapping等是Spring提供的处理HTTP请求的注解,
* 1.当它们与@RestController注解一起使用时,表示接受其他服务发送过来的请求。
* 2.当它们与@FeignClient注解一起使用时,表示给其他服务发送请求。
*
* @param id 商品id
* @return 商品信息
*/
@GetMapping("/product/query/{id}")
Product getProductById(@PathVariable(value = "id") Long id);
}
3. 创建FeignClient接口的兜底回调实现类,用于返回默认数据。
package com.springcloud.demo.order.feign.fallback;
import java.math.BigDecimal;
import com.springcloud.demo.order.feign.ProductFeignClient;
import com.springcloud.demo.product.bean.Product;
import org.springframework.stereotype.Component;
@Component
public class ProductFeignClientFallback implements ProductFeignClient {
@Override
public Product getProductById(Long id) {
System.out.println("ProductFeignClient的兜底回调......");
Product product = new Product();
product.setId(id);
product.setPrice(new BigDecimal("0"));
product.setProductName("未知商品");
product.setNum(0);
return product;
}
}
4. 在sentinel控制台给OpenFeign调用类型的资源配置规则。


5. 通过postman多次调用service-order服务创建订单的接口,观察响应结果,得到兜底数据。

3、sentinel规则
1、流量控制(FlowRule)


| 模式类型 | 适用场景 | 特点 | 配置方式 |
| 直接模式 | 单资源独立控制 | 最基础模式,直接针对资源名限流 | FlowRule 指定资源名 |
| 关联模式 | 资源间依赖控制 | 基于关联资源状态控制目标资源 | DegradeRule 配置关联规则 |
| 链路模式 | 全链路流量控制 | 按调用链整体限流(需Tracer) | FlowRule 设置 clusterMode=true |
直接模式(Direct Mode)
1. 核心特性
- 独立控制:每个资源单独配置流控规则。
- 精确匹配:精确到具体的方法或接口调用。
2. 典型场景
- API接口频率限制
- 秒杀活动独立限流
3. 配置示例
// 创建规则
FlowRule rule = new FlowRule("/order/create")
.setCount(10) // QPS阈值
.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 应用规则
DegradeRuleManager.loadRules(Arrays.asList(rule));
关联模式(Association Mode)
1. 核心特性
- 资源联动:基于关联资源的流控状态影响目标资源。
- 状态传递:关联资源被限流时,目标资源自动降级。
2. 典型场景
- 订单服务依赖支付服务,支付服务限流时自动降级订单服务。
- 库存服务影响商品服务。
3. 配置示例
DegradeRule rule = new DegradeRule("orderService") // 关联资源
.setGrade(RuleConstant.DEGRADE_GRADE_RT) // 按RT降级
.setCount(500) // 阈值
.setTimeWindow(10); // 统计窗口
// 应用规则
DegradeRuleManager.loadRules(Arrays.asList(rule));
链路模式(Cluster Mode)
1. 核心特性
- 全局视角:对调用链整体进行流量控制。
- 全链路关联:需开启Tracer组件追踪调用链。
2. 典型场景
- 微服务集群整体限流
- 分布式系统全局QPS控制
3. 配置示例
FlowRule rule = new FlowRule("clusterResource")
.setCount(1000)
.setGrade(RuleConstant.FLOW_GRADE_CLUSTER_QPS); // 集群模式
// 应用规则
FlowRuleManager.loadRules(Arrays.asList(rule));
流量控制代码示例
流控模式有三种:直接、关联和链路,默认是直接流控模式,在上面讲解sentinel资源异常处理时就是用的这种模式,此处不再赘述。
1、流量控制-链路模式
现在有个需求:创建订单有两种方式,日常普通创建订单和秒杀活动创建订单,只对秒杀创建订单的方式进行流控限制,而普通创建订单的方式不做流控限制。对于这个需求可以使用直接流控模式,对订单秒杀接口/order/seckill(接口名称也是资源名称)配置QPS阈值即可。也可以使用链路流控模式实现这个需求。如果使用链路流控模式实现这个需求,需要关闭统一web上下文配置。
spring.cloud.sentinel.web-context-unify的值默认为true,表示开启统一web上下文。如果开启统一web上下文,则同一个微服务下的所有接口共用一个web上下文,该web上下文的资源名是sentinel_spring_web_context,如下图所示。

1. 在service-order服务的OrderController类中新增秒杀创建订单的接口。
package com.springcloud.demo.order.controller;
import com.springcloud.demo.common.Result;
import com.springcloud.demo.order.bean.Order;
import com.springcloud.demo.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
/**
* POST请求接收JSON数据时使用简单类型(Integer、String等)不能自动填充数据
* 必须要封装成实体类或者使用Map接收,而且使用Map接收时还要注明泛型
*
* @param map 请求参数
* @return 订单信息
*/
@PostMapping("/create")
public Result createOrder(@RequestBody Map<String, Object> map) {
Long userId = Long.parseLong(String.valueOf(map.get("userId")));
Long productId = Long.parseLong(String.valueOf(map.get("productId")));
Order order = orderService.createOrder(userId, productId);
return Result.ok(order);
}
/**
* 秒杀场景创建订单
*
* @param map 请求参数
* @return 订单信息
*/
@PostMapping("/seckill")
public Result seckill(@RequestBody Map<String, Object> map) {
Long userId = Long.parseLong(String.valueOf(map.get("userId")));
Long productId = Long.parseLong(String.valueOf(map.get("productId")));
Order order = orderService.createOrder(userId, productId);
order.setId(Long.MAX_VALUE);
return Result.ok(order);
}
@GetMapping("/config")
public Result getConfig() {
return Result.ok(orderService.getConfig());
}
}
2. 在application.yml文件配置spring.cloud.sentinel.web-context-unify: false,关闭统一web上下文。
server:
port: 8000
spring:
profiles:
active: dev # 通过修改spring.profiles.active属性的值来选择不同环境的配置
include: feign # 引入application-feign.yml文件
application:
name: service-order
cloud:
nacos:
# 配置nacos地址,将微服务注册到nacos上
server-addr: 127.0.0.1:8848
config:
import-check:
enabled: false
# 如果没有配置spring.profiles.active,则默认使用public命名空间
namespace: ${spring.profiles.active:public}
sentinel:
transport:
# 配置sentinel地址,连接sentinel
dashboard: localhost:8080
eager: true # 服务启动就连接sentinel,默认是第一次调用服务才会连接sentinel
web-context-unify: false # 是否统一web上下文,默认为true
logging:
level:
# 给com.springcloud.demo.order.feign包下的类设置日志级别为debug
com.springcloud.demo.order.feign: debug
---
spring:
config:
import:
- nacos:common.properties?group=service-order
- nacos:database.properties?group=service-order
activate:
on-profile: dev
---
spring:
config:
import:
- nacos:common.properties?group=service-order
activate:
on-profile: test
---
spring:
config:
import:
- nacos:database.properties?group=service-order
activate:
on-profile: product
3. 重启service-order和service-product微服务,通过postman调用/order/create和/order/seckill两个接口,在sentinel控制台可以看到这两个接口资源,这两个接口会使用共同的createOrder资源。如果使用直接流控模式对createOrder资源进行sentinel流控限制,会对/order/create和/order/seckill两个接口都做限流。可以使用直接流控模式对/order/seckill资源精确流控。

4. 对createOrder资源使用链路流控模式配置/order/seckill作为入口资源,sentinel实际只对/order/seckill接口进行限流,不会对/order/create接口进行限流,即使/order/create接口中也用到了createOrder资源。

5. 通过postman分别多次调用/order/create和/order/seckill接口,发现前者一直成功,后者偶尔会触发sentinel限流而返回兜底数据。

2、流量控制-关联模式
现在有个需求:当对数据库只读或只写时不做流量控制,如果频繁写数据库时,对读数据库操作进行流量控制,优先执行写数据库操作。例如有两个接口/order/create和/order/config,当调用/order/create接口频繁创建订单写数据库时,对查询订单配置/order/config接口进行限流。如果只调用查询订单配置接口,不会触发限流。

通过postman先对/order/create接口频繁调用,然后立即调用/order/config接口,观察是否会触发sentinel流控限制。

2、熔断降级(DegradeRule)


断路器工作原理:
服务之间正常调用时断路器状态是关闭的,若下游服务异常导致服务之间调用异常,满足熔断触发条件(达到慢调用比例、异常比例或异常数)时,断路器状态变成打开,在设置的熔断时长内(例如30秒),上游服务会直接返回错误或使用兜底数据,不会真正的调用下游服务。当设置的熔断时长结束之后,此时断路器状态变成半开,上游服务再次调用下游服务时,先放行一个请求尝试调用下游服务(其他调用依然会直接返回错误或使用兜底数据),若这个调用可以正常返回,则断路器状态变成关闭,后续请求都会真正的调用下游服务。若这个调用返回异常,则断路器状态变成打开,再次设置熔断时长,在熔断时长内,上游服务会直接返回错误或使用兜底数据,不会真正的调用下游服务。
熔断触发条件有三种:慢调用比例、异常比例和异常数熔断策略。
1、慢调用比例熔断策略有以下几个参数:
- 最大RT:最大响应时间(单位:ms),慢调用的阈值,超过该值的请求是“慢调用”。
- 比例阈值:慢调用占比达到该值时触发熔断(取值范围是0.0-1.0)。
- 熔断时长:触发熔断后,服务进入“熔断状态”的持续时间(单位:s)。
- 最小请求数:统计时长内,请求量需达到该值才可能触发熔断(避免“小流量下的误判”)。
- 统计时长:统计慢调用比例的时间窗口(单位:ms)。
当满足以下条件时,Sentinel 会触发熔断:
- 在统计时长内,请求量 ≥ 最小请求数(如 5 次),若请求量 < 最小请求数,即使慢调用比例达标也不触发熔断;
- 慢调用比例 ≥ 比例阈值(如 80%)。
触发熔断后,后续请求会被降级(如直接返回默认值、抛异常、跳过服务调用等)。
如下图所示,在统计时长5秒之内,若请求次数大于等于5,且有80%的请求都是慢调用(响应时间超过1秒的是慢调用),则sentinel就会触发熔断,熔断时长是30秒,在这30秒内,后续请求会被降级,不会调用下游异常服务。

2、异常比例熔断策略有以下几个参数:
- 比例阈值:若请求中异常(如500错误、超时)的比例达到“比例阈值”,则触发熔断(取值范围是0.0-1.0)。
- 熔断时长:触发熔断后,服务进入“熔断状态”的持续时间(单位:s)。
- 最小请求数:统计时长内,请求量需达到该值才可能触发熔断(避免“小流量下的误判”)。
- 统计时长:统计异常比例的时间窗口(单位:ms)。
当满足以下条件时,Sentinel 会触发熔断:
- 在统计时长内,请求量 ≥ 最小请求数(如 5 次),若请求量 < 最小请求数,即使异常比例达标也不触发熔断;
- 异常比例 ≥ 比例阈值(如 50%)。
触发熔断后,后续请求会被降级(如直接返回默认值、抛异常、跳过服务调用等)。
如下图所示,在统计时长5秒之内,若请求次数大于等于5,且有50%的请求都是异常调用(响应返回错误或响应超时等),则sentinel就会触发熔断,熔断时长是30秒,在这30秒内,后续请求会被降级,不会调用下游异常服务。

3、异常数熔断策略有以下几个参数:
- 异常数:若异常(如500错误、超时异常)的请求数量达到“异常数”,则触发熔断。
- 熔断时长:触发熔断后,服务进入“熔断状态”的持续时间(单位:s)。
- 最小请求数:统计时长内,请求量需达到该值才可能触发熔断(避免“小流量下的误判”)。
- 统计时长:统计异常比例的时间窗口(单位:ms)。
当满足以下条件时,Sentinel 会触发熔断:
- 在统计时长内,请求量 ≥ 最小请求数(如 5 次),若请求量 < 最小请求数,即使异常数达标也不触发熔断;
- 异常的请求个数 ≥ 异常数(如10)。
触发熔断后,后续请求会被降级(如直接返回默认值、抛异常、跳过服务调用等)。
如下图所示,在统计时长5秒之内,若请求次数大于等于5,且超过10个请求都是异常调用(响应返回错误),则sentinel就会触发熔断,熔断时长是30秒,在这30秒内,后续请求会被降级,不会调用下游异常服务。

熔断降级代码示例
1. 在application-feign.yml或application.yml文件中开启sentinel的熔断降级功能。
spring:
cloud:
openfeign:
client:
config:
default: # 默认连接超时和读取超时都设置成30秒,对所有feign客户端生效
logger-level: full
connect-timeout: 30000
read-timeout: 30000
service-product: # 被调用的服务名称,@FeignClient注解value属性值
logger-level: full
connect-timeout: 3000
read-timeout: 5000
# request-interceptors:
# - com.springcloud.demo.order.interceptor.XTokenRequestInterceptor
feign:
sentinel:
enabled: true # 开启熔断降级功能
2. 上面在OrderServiceImpl类的createOrder方法中,通过ProductFeignClient接口远程调用service-product服务的getProductById方法获取商品信息,ProductFeignClient接口如下所示。当getProductById方法出现异常时,通过@FeignClient注解的fallback属性指定的兜底回调返回默认的数据。
package com.springcloud.demo.order.feign;
import com.springcloud.demo.order.feign.fallback.ProductFeignClientFallback;
import com.springcloud.demo.product.bean.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @FeignClient是Spring Cloud提供的一个声明式HTTP客户端注解,主要用于简化微服务之间的远程调用。
* 通过简单的接口定义和注解配置,即可实现服务间的HTTP调用,开发者无需手动编写HTTP请求代码。
* 多种参数配置:
* value/name:指定要调用的服务名称
* url:直接指定服务URL,绕过服务发现,使用该方式调用没有在Nacos上注册的第三方服务
* fallback:定义调用失败时的降级处理类
* configuration:自定义Feign客户端配置
*/
@FeignClient(value = "service-product", fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {
/**
* 调用"/product/{id}"接口发送请求获取结果
* @GetMapping、@PostMapping、@PutMapping等是Spring提供的处理HTTP请求的注解,
* 1.当它们与@RestController注解一起使用时,表示接受其他服务发送过来的请求。
* 2.当它们与@FeignClient注解一起使用时,表示给其他服务发送请求。
*
* @param id 商品id
* @return 商品信息
*/
@GetMapping("/product/query/{id}")
Product getProductById(@PathVariable(value = "id") Long id);
}
3. 创建FeignClient接口的兜底回调实现类,用于返回默认数据。
package com.springcloud.demo.order.feign.fallback;
import java.math.BigDecimal;
import com.springcloud.demo.order.feign.ProductFeignClient;
import com.springcloud.demo.product.bean.Product;
import org.springframework.stereotype.Component;
@Component
public class ProductFeignClientFallback implements ProductFeignClient {
@Override
public Product getProductById(Long id) {
System.out.println("ProductFeignClient的兜底回调......");
Product product = new Product();
product.setId(id);
product.setPrice(new BigDecimal("0"));
product.setProductName("未知商品");
product.setNum(0);
return product;
}
}
4. 在sentinel控制台给OpenFeign调用类型的资源配置规则。


5. 通过postman多次调用service-order服务创建订单的接口,观察响应结果,得到兜底数据。

3、热点参数限流
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制。
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制。
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。
热点参数限流用于限制特定接口中某参数的高频请求(如订单创建接口的用户 ID 参数),避免单个参数值(如高频下单的用户)引发系统负载激增,保障服务稳定性。
在sentinel-dashboard界面新增和编辑热点规则。


热点规则有以下几个参数:
- 资源名:需与 Sentinel 配置中定义的资源名一致(如接口路径 /order/create),确保限流规则精准绑定到目标接口。
- 限流模式:选择 QPS 模式(基于请求速率限制)或 线程数模式(基于并发线程数限制),根据业务场景选择(如高频请求场景选 QPS)。
- 参数索引:指定接口请求参数的索引位置(如 GET 请求的查询参数、POST 请求的 JSON 字段索引),需与接口参数结构匹配,索引0表示第一个参数,索引1表示第二个参数。
- 单机阈值:单台机器对该参数的请求速率上限(如设置为 1,表示每秒最多允许 1 次该参数的请求)。
- 统计窗口时长:统计请求速率的时间窗口(如 1 秒,表示按 1 秒内请求量计算速率)。
- 是否集群:若为分布式集群,需勾选以集群维度统计请求(单机模式仅统计本机请求)。
- 参数例外项:针对特定参数值设置例外规则(如用户 ID 为 5 时,允许更高限流阈值 10000000),避免误限高频正常请求。
热点参数限流代码示例
现在有个需求:在秒杀场景中,除了超级VIP用户,其他用户每秒最多只允许抢购一单。
假设秒杀接口/order/seckill的请求参数是(Long userId, Long productId),如下所示:
/**
* 使用@SentinelResource注解定义一个sentinel资源,用于标记需要受保护的资源。
* value:资源名称,在控制台配置规则的依据,该属性是必须的。
* entryType:流量方向:IN(入口)/OUT(出口)。
* blockHandler:处理BlockException异常的降级方法名。
* fallback:业务异常时的降级方法名。
* defaultFallback:默认降级方法名(无参或单Throwable参数)。
* exceptionsToIgnore:忽略的异常类型(不触发fallback)。
* 被@SentinelResource注解标记为资源的业务方法,如果违反资源的规则,会抛出BlockException异常,
* 则按顺序依次使用blockHandler、fallback或defaultFallback指定的方法进行兜底回调,
* 若这三个属性都没有指定兜底回调方法,则抛出BlockException异常信息。
*
* @param userId 用户id
* @param productId 商品id
* @return 订单信息
*/
@PostMapping("/seckill")
@SentinelResource(value = "seckill-order", fallback = "seckillFallback")
public Result seckill(Long userId, Long productId) {
Order order = orderService.createOrder(userId, productId);
order.setId(Long.MAX_VALUE);
return Result.ok(order);
}
// 方法参数需要与原方法相同,可以在最后添加BlockException或Throwable
// 若原方法上@SentinelResource注解使用的是blockHandler,则该方法添加BlockException,
// 若原方法上@SentinelResource注解使用的是fallback,则该方法添加Throwable
public Result seckillFallback(Long userId, Long productId, Throwable exception) {
Order order = new Order();
order.setId(0L);
order.setTotalAmount(new BigDecimal("0"));
order.setUserId(userId);
order.setNickName("异常信息:" + exception.getClass());
return Result.ok(order);
}
对于这个需求,在sentinel-dashboard界面给"seckill-order"资源创建热点规则,设置参数索引为0,表示一个参数userId,单机阈值和统计窗口时长都是1(用户每秒最多只允许抢购一单)。另外假设userId=5是超级VIP,需要设置参数例外项,将userId=5的限流阈值调成非常大的一个数值。
若接口参数是基础类型或String类型,Sentinel 会将它们作为参数传入 SphU.entry(res, args)。但是若接口参数是对象类型,则该对象必须实现ParamFlowArgument 接口,并重写 paramFlowKey 方法来提供热点参数的键值。若接口参数原来是Map类型,也需要改造成对象类型,且这个对象需要实现ParamFlowArgument 接口。
让OrderParam对象实现ParamFlowArgument 接口:
package com.springcloud.demo.order.pojo;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowArgument;
import lombok.Data;
@Data
public class OrderParam implements ParamFlowArgument {
private Long userId;
private Long productId;
@Override
public Object paramFlowKey() {
// 使用 userId 作为热点键
return userId;
}
}
/**
* 秒杀场景创建订单
*
* @param orderParam 请求参数
* @return 订单信息
*/
@PostMapping("/seckill")
@SentinelResource(value = "seckill-order", fallback = "seckillFallback")
public Result seckill(@RequestBody OrderParam orderParam) {
Order order = orderService.createOrder(orderParam.getUserId(), orderParam.getProductId());
order.setId(Long.MAX_VALUE);
return Result.ok(order);
}
public Result seckillFallback(OrderParam orderParam, Throwable exception) {
Order order = new Order();
order.setId(0L);
order.setTotalAmount(new BigDecimal("0"));
order.setUserId(orderParam.getUserId());
order.setNickName("异常信息:" + exception.getClass());
return Result.ok(order);
}
通过postman频繁调用秒杀接口/order/seckill,观察响应结果。

4、授权规则
授权规则用于控制调用方对资源的访问权限,通过黑白名单机制实现:
- 白名单:允许指定来源(origin)的请求访问资源。
- 黑名单:禁止指定来源的请求访问资源。
实现方式:
- 请求来源标识:通过实现RequestOriginParser接口解析请求头或参数中的origin值。
- 动态配置:支持与Nacos等配置中心集成,实现规则动态更新。
典型场景:
- 限制只有网关发起的请求能访问微服务,防止绕过网关的直接调用。
- 按用户角色或服务划分访问权限。

5、系统规则
系统规则是从应用整体维度进行保护的规则,仅对入口流量(如Web服务或Dubbo服务端接收的请求)生效。主要包含以下阈值类型:
- Load(仅Linux/Unix生效):当系统load超过阈值且并发线程数超过容量时触发保护。
- RT(平均响应时间):入口请求的RT超过阈值时触发。
- 线程数:并发线程数超过阈值时触发。
- 入口QPS:所有入口流量的QPS总和超过阈值时触发。
- CPU使用率:系统CPU使用率超过阈值时触发。
系统规则通过监控上述指标,在保证系统最大吞吐量的同时维持整体稳定性。

4、sentinel规则持久化
sentinel的所有规则都是内存存储的,应用重启后所有规则都会丢失,在生产环境下,必须确保这些规则的持久化,避免丢失。

Sentinel为了实现规则的持久化管理,提供了ReadableDataSrouce(配置读取)与WritableDataSource(配置写入)两个接口,通过这两个接口可以向指定的存储设备中实现规则的读写处理。

如果要开发一个系统,这个系统可以由管理人员在服务应用部署上线之前,就可以进行所有限流规则的维护,这个时候一定需要有一个完善的管理界面(排除没有完善管理界面的存储终端:Redis、ZooKeeper),所以最佳的做法就是使用 Nacos,因为 Nacos有完善的管理界面,而且其本身是提供有 JSON 数据(各种数据类型都是支持的)的直接存储,最重要的是 Nacos 还是 SpringCloudAlibaba 套件之中最为重要的注册中心。
Sentinel 提供的 DataSource 是一个逻辑上的概念,具体的存储可以是关系型数据库、文件、ZooKeeper、Redis、Nacos 等存储终端,在终端中可以保存所需要的限流规则,而对于 DataSource 的操作形式有两种:拉模式(Pull-Based)和推模式(Push-Based)。
sentinel支持三种规则管理模式:
- 原始模式:Sentinel的默认模式,将规则保存在内存,重启应用服务会导致规则丢失。
- pull模式:sentinel客户端(即应用服务)主动向规则管理中心定期轮询拉取规则,规则中心可以是RDBMS、文件等,虽然此种方式简单,但是却无法及时获取到配置更新。
- push模式:所有的规则由配置中心(Nacos、Zookeeper、Redis 等)统一推送,sentinel客户端(即应用服务)通过注册监听器的方式监听配置中心,获取配置变更的推送消息,这样可以更好的保持配置的实时性和一致性。


ReadableDataSource源码如下所示:
package com.alibaba.csp.sentinel.datasource;
import com.alibaba.csp.sentinel.property.SentinelProperty;
public interface ReadableDataSource<S, T> {
// 加载配置
T loadConfig() throws Exception;
// 读取原生配置的数据项
S readSource() throws Exception;
// 获取Sentinel配置属性
SentinelProperty<T> getProperty();
// 关闭读取的数据源
void close() throws Exception;
}
在Sentinel中所有与配置属性有关的更新处理操作全部都是由SentinelProperty接口完成的,而这个接口需要及时处理数据更新,最佳的做法就是通过nacos实现处理。
package com.alibaba.csp.sentinel.property;
public interface SentinelProperty<T> {
// 添加监听器,监听配置属性变更
void addListener(PropertyListener<T> var1);
// 移除监听器
void removeListener(PropertyListener<T> var1);
// 更新属性值
boolean updateValue(T var1);
}
1、规则配置属性

Sentinel各个规则(流控、熔断降级、授权、系统、热点规则)的配置信息具体有哪些属性,该如何配置这些属性,需要查看SentinelAutoConfiguration自动装配的源码,在这个类中用到了各个规则对应的实体类(FlowRule、DegradeRule、AuthorityRule、SystemRule、ParamFlowRule),规则实体类中封装了每个规则的属性。
SentinelAutoConfiguration的源码如下所示:
package com.alibaba.cloud.sentinel.custom;
import com.alibaba.cloud.sentinel.SentinelProperties;
import com.alibaba.cloud.sentinel.datasource.converter.JsonConverter;
import com.alibaba.cloud.sentinel.datasource.converter.XmlConverter;
import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* @author xiaojing
* @author jiashuai.xie
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
* @author freeman
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.sentinel.enabled", matchIfMissing = true)
@EnableConfigurationProperties(SentinelProperties.class)
public class SentinelAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
@ConditionalOnProperty(name = "resttemplate.sentinel.enabled", havingValue = "true",
matchIfMissing = true)
public static SentinelBeanPostProcessor sentinelBeanPostProcessor(
ApplicationContext applicationContext) {
return new SentinelBeanPostProcessor(applicationContext);
}
@Bean
@ConditionalOnMissingBean
public SentinelDataSourceHandler sentinelDataSourceHandler(
DefaultListableBeanFactory beanFactory, SentinelProperties sentinelProperties,
Environment env) {
return new SentinelDataSourceHandler(beanFactory, sentinelProperties, env);
}
@ConditionalOnClass(ObjectMapper.class)
@Configuration(proxyBeanMethods = false)
protected static class SentinelConverterConfiguration { // 转换JSON格式的规则配置
@Configuration(proxyBeanMethods = false)
protected static class SentinelJsonConfiguration {
private ObjectMapper objectMapper = new ObjectMapper();
public SentinelJsonConfiguration() {
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
}
@Bean("sentinel-json-flow-converter")
public JsonConverter jsonFlowConverter() {
return new JsonConverter(objectMapper, FlowRule.class);
}
@Bean("sentinel-json-degrade-converter")
public JsonConverter jsonDegradeConverter() {
return new JsonConverter(objectMapper, DegradeRule.class);
}
@Bean("sentinel-json-system-converter")
public JsonConverter jsonSystemConverter() {
return new JsonConverter(objectMapper, SystemRule.class);
}
@Bean("sentinel-json-authority-converter")
public JsonConverter jsonAuthorityConverter() {
return new JsonConverter(objectMapper, AuthorityRule.class);
}
@Bean("sentinel-json-param-flow-converter")
public JsonConverter jsonParamFlowConverter() {
return new JsonConverter(objectMapper, ParamFlowRule.class);
}
}
@ConditionalOnClass(XmlMapper.class)
@Configuration(proxyBeanMethods = false)
protected static class SentinelXmlConfiguration { // 转换XML格式的规则配置
private XmlMapper xmlMapper = new XmlMapper();
public SentinelXmlConfiguration() {
xmlMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
}
@Bean("sentinel-xml-flow-converter")
public XmlConverter xmlFlowConverter() {
return new XmlConverter(xmlMapper, FlowRule.class);
}
@Bean("sentinel-xml-degrade-converter")
public XmlConverter xmlDegradeConverter() {
return new XmlConverter(xmlMapper, DegradeRule.class);
}
@Bean("sentinel-xml-system-converter")
public XmlConverter xmlSystemConverter() {
return new XmlConverter(xmlMapper, SystemRule.class);
}
@Bean("sentinel-xml-authority-converter")
public XmlConverter xmlAuthorityConverter() {
return new XmlConverter(xmlMapper, AuthorityRule.class);
}
@Bean("sentinel-xml-param-flow-converter")
public XmlConverter xmlParamFlowConverter() {
return new XmlConverter(xmlMapper, ParamFlowRule.class);
}
}
}
}
AbstractRule的部分源码,省略了一些方法:
package com.alibaba.csp.sentinel.slots.block;
import java.util.Objects;
/**
* Abstract rule entity.
*
* @author youji.zj
* @author Eric Zhao
*/
public abstract class AbstractRule implements Rule {
/**
* rule id.
*/
private Long id;
/**
* Resource name.
*/
private String resource;
/**
* <p>
* Application name that will be limited by origin.
* The default limitApp is {@code default}, which means allowing all origin apps.
* </p>
* <p>
* For authority rules, multiple origin name can be separated with comma (',').
* </p>
*/
private String limitApp;
/**
* Whether to match resource names according to regular rules
*/
private boolean regex;
}
1、流控规则配置属性
FlowRule的部分源码,省略了一些方法:
package com.alibaba.csp.sentinel.slots.block.flow;
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
public class FlowRule extends AbstractRule {
public FlowRule() {
super();
setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
}
public FlowRule(String resourceName) {
super();
setResource(resourceName);
setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
}
/**
* The threshold type of flow control (0: thread count, 1: QPS).
*/
private int grade = RuleConstant.FLOW_GRADE_QPS;
/**
* Flow control threshold count.
*/
private double count;
/**
* Flow control strategy based on invocation chain.
*
* {@link RuleConstant#STRATEGY_DIRECT} for direct flow control (by origin);
* {@link RuleConstant#STRATEGY_RELATE} for relevant flow control (with relevant resource);
* {@link RuleConstant#STRATEGY_CHAIN} for chain flow control (by entrance resource).
*/
private int strategy = RuleConstant.STRATEGY_DIRECT;
/**
* Reference resource in flow control with relevant resource or context.
*/
private String refResource;
/**
* Rate limiter control behavior.
* 0. default(reject directly), 1. warm up, 2. rate limiter, 3. warm up + rate limiter
*/
private int controlBehavior = RuleConstant.CONTROL_BEHAVIOR_DEFAULT;
private int warmUpPeriodSec = 10;
/**
* Max queueing time in rate limiter behavior.
*/
private int maxQueueingTimeMs = 500;
private boolean clusterMode;
/**
* Flow rule config for cluster mode.
*/
private ClusterFlowConfig clusterConfig;
/**
* The traffic shaping (throttling) controller.
*/
private TrafficShapingController controller;
}
结合AbstractRule和FlowRule的源码可知,流控规则的配置属性大致如下所示。
[
{
"resource": "/order/create", // 资源名称
"limitApp": "default", // 应用来源,使用默认值default
"grade": 1, // 阈值类型,0:线程数量,1:QPS
"count": 2, // 单机阈值(若"grade": 1,"count": 2,表示每秒最多允许两个请求)
"strategy": 0, // 流控模式,0:直接,1:关联,2:链路
"refResource": 0, // 关联资源,strategy =1关联模式或strategy =2链路模式需要该字段
"controlBehavior": 0, // 流控效果,0:快速失败,1:WarmUp,2:排队等待
"warmUpPeriodSec": 10, // 预热时长,controllBehavior=1(WarmUp)需要该字段
"maxQueueingTimeMs": 500, // 队列超时时间,controllBehavior=2(排队等待)需要该字段
"clusterMode": false, // 采用非集群模式
"clusterConfig": { // 集群配置,clusterMode=true集群模式需要该配置
"thresholdType": 0, // 集群阈值模式,0:单机均摊,1:总体阈值
"fallbackToLocalWhenFail": true // 失败退化,如果Token Server不可用是否退化到单机限流
}
}
]
流控规则根据是否集群、流控模式和流控效果的不同组合有以下几种形式,规则配置属性根据不同的组合形式大致相同,略有差异。
1. 单机-直接-快速失败

对应的流控规则配置属性如下所示:
[
{
"resource": "/order/create",
"limitApp": "default",
"grade": 1,
"count": 2,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
在Nacos上创建配置:

2. 单机-直接-WarmUp

3. 单机-直接-排队等待

4. 单机-关联-快速失败

5. 单机-关联-WarmUp

6. 单机-关联-排队等待

7、单机-链路-快速失败/WarmUp/排队等待

8、集群模式

2、熔断降级规则配置属性
DegradeRule的部分源码,省略了一些方法:
package com.alibaba.csp.sentinel.slots.block.degrade;
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import java.util.Objects;
public class DegradeRule extends AbstractRule {
public DegradeRule() {}
public DegradeRule(String resourceName) {
setResource(resourceName);
}
/**
* Circuit breaking strategy (0: average RT, 1: exception ratio, 2: exception count).
*/
private int grade = RuleConstant.DEGRADE_GRADE_RT;
/**
* Threshold count. The exact meaning depends on the field of grade.
* <ul>
* <li>In average RT mode, it means the maximum response time(RT) in milliseconds.</li>
* <li>In exception ratio mode, it means exception ratio which between 0.0 and 1.0.</li>
* <li>In exception count mode, it means exception count</li>
* <ul/>
*/
private double count;
/**
* Recovery timeout (in seconds) when circuit breaker opens. After the timeout, the circuit breaker will
* transform to half-open state for trying a few requests.
*/
private int timeWindow;
/**
* Minimum number of requests (in an active statistic time span) that can trigger circuit breaking.
*
* @since 1.7.0
*/
private int minRequestAmount = RuleConstant.DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT;
/**
* The threshold of slow request ratio in RT mode.
*
* @since 1.8.0
*/
private double slowRatioThreshold = 1.0d;
/**
* The interval statistics duration in millisecond.
*
* @since 1.8.0
*/
private int statIntervalMs = 1000;
}
结合AbstractRule和DegradeRule的源码可知,熔断降级规则的配置属性大致如下所示。
[
{
"resource": " GET:http://service-product/product/query/{id}", // 资源名称
"grade": 0, // 熔断策略,0:慢调用比例,1:异常比例,2:异常数
"count": 1000, // 阈值数量,grade=0,count表示最大响应时间RT,单位毫秒;grade=1,count表示比例阈值,取值范围0.0-1.0;grade=2,count表示异常数
"slowRatioThreshold": 1.0, // 比例阈值,grade=0慢调用比例熔断策略时需要该属性
"timeWindow": 30, // 熔断时长,单位秒
"minRequestAmount": 5, // 最小请求数
"statIntervalMs": 5000 // 统计时长,单位毫秒
}
]
在前文已对不同熔断策略的各个参数进行详细解释,此处不再赘述。
在Nacos上创建配置:

3、热点参数限流规则配置属性
ParamFlowRule的部分源码,省略了一些方法:
package com.alibaba.csp.sentinel.slots.block.flow.param;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
public class ParamFlowRule extends AbstractRule {
public ParamFlowRule() {}
public ParamFlowRule(String resourceName) {
setResource(resourceName);
}
/**
* The threshold type of flow control (0: thread count, 1: QPS).
*/
private int grade = RuleConstant.FLOW_GRADE_QPS;
/**
* Parameter index.
*/
private Integer paramIdx;
/**
* The threshold count.
*/
private double count;
/**
* Traffic shaping behavior (since 1.6.0).
*/
private int controlBehavior = RuleConstant.CONTROL_BEHAVIOR_DEFAULT;
private int maxQueueingTimeMs = 0;
private int burstCount = 0;
private long durationInSec = 1;
/**
* Original exclusion items of parameters.
*/
private List<ParamFlowItem> paramFlowItemList = new ArrayList<ParamFlowItem>();
/**
* Parsed exclusion items of parameters. Only for internal use.
*/
private Map<Object, Integer> hotItems = new HashMap<Object, Integer>();
/**
* Indicating whether the rule is for cluster mode.
*/
private boolean clusterMode = false;
/**
* Cluster mode specific config for parameter flow rule.
*/
private ParamFlowClusterConfig clusterConfig;
}
结合AbstractRule和ParamFlowRule的源码可知,热点参数限流规则的配置属性如下所示。
[
{
"resource": "/order/create", // 资源名称
"grade": 1, // 限流模式,0:线程数量,1:QPS(默认是QPS)
"paramIdx": 0, // 参数索引
"count": 1, // 单机阈值
"controlBehavior": 0, // 流控效果,0:快速失败,1:WarmUp,2:排队等待
"maxQueueingTimeMs": 500, // 队列超时时间,controllBehavior=2(排队等待)需要该字段
"durationInSec": 1, // 统计窗口时长,单位秒
"clusterMode": false, // 采用非集群模式
"clusterConfig": { // 集群配置,clusterMode=true集群模式需要该配置
"thresholdType": 0, // 集群阈值模式,0:单机均摊,1:总体阈值
"fallbackToLocalWhenFail": true // 失败退化,如果Token Server不可用是否退化到单机限流
},
"paramFlowItemList": [ // 参数例外项
{
"object": "",
"count": 0, // 参数值
"classType": "" // 参数类型
}
]
}
]
在Nacos上创建配置:

4、授权规则配置属性
AuthorityRule的部分源码,省略了一些方法:
package com.alibaba.csp.sentinel.slots.block.authority;
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
public class AuthorityRule extends AbstractRule {
/**
* Mode: 0 for whitelist; 1 for blacklist.
*/
private int strategy = RuleConstant.AUTHORITY_WHITE;
}
结合AbstractRule和AuthorityRule的源码可知,热点参数限流规则的配置属性如下所示。
[
{
"resource": "/order/create", // 资源名称
"limitApp": 0, // 流控应用名称,多个应用名称用逗号分隔
"strategy": 0 // 授权类型,0:白名单,1:黑名单
}
]
5、系统规则配置属性
SystemRule的部分源码,省略了一些方法:
package com.alibaba.csp.sentinel.slots.system;
import com.alibaba.csp.sentinel.slots.block.AbstractRule;
public class SystemRule extends AbstractRule {
/**
* negative value means no threshold checking.
*/
private double highestSystemLoad = -1;
/**
* cpu usage, between [0, 1]
*/
private double highestCpuUsage = -1;
private double qps = -1;
private long avgRt = -1;
private long maxThread = -1;
}
结合AbstractRule和SystemRule的源码可知,热点参数限流规则的配置属性如下所示。
[
{
"qps": 1 // 阈值类型(平均RT、最大线程数、QPS、最大CPU使用率)及其对应的阈值
}
]

2、从Nacos同步规则至Sentinel


当前Sentinel Dashboard默认仅支持从Nacos同步规则至内存,暂不支持在sentinel-dashboard控制台修改后的规则主动同步回Nacos。
在nacos配置中心创建sentinel规则主要有以下几步:
1、启动nacos,打开nacos界面,创建命名空间

记录命名空间id:677d9fd4-2dea-4391-891d-52671399ed48,在下面配置文件中需要使用。
2、在nacos上创建规则配置
示例1:创建限流规则配置

[ // 可以有多个限流配置,所以使用JSON数组
{ // 配置某一个具体的限流规则
"resource": "/order/create", // 资源名称
"limitApp": "default", // 应用来源,使用默认值default
"grade": 1, // 阈值类型,0:线程数量,1:QPS
"count": 1, // 单机阈值(若"grade": 1,"count": 1,表示每秒最多允许一个请求)
"strategy": 0, // 流控模式,0:直接,1:关联,2:链路
"controlBehavior": 0, // 流控效果,0:快速失败,1:WarmUp,2:排队等待
"clusterMode": false // 采用非集群模式
}
]
这些配置属性与在sentinel控制台创建流控规则的界面是一一对应的,如下图所。之前是在sentinel控制台创建规则的,现在是在nacos界面通过JSON数据格式进行规则配置的。

示例2:创建熔断降级规则

[
{
"resource": "GET:http://service-product/product/query/{id}",
"grade": 0,
"count": 1000,
"slowRatioThreshold": 1.0,
"timeWindow": 30,
"minRequestAmount": 5,
"statIntervalMs": 5000
}
]

3、在services模块的pom.xml文件中引入sentinel规则持久化保存到nacos的依赖
<!-- sentinel配置规则持久化 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.8</version>
</dependency>
4、在service-order的application.yml文件中添加如下配置。注意:不要使用username和password属性连接nacos,sentinel是无法通过认证连接到nacos的。

service-order的application.yml文件完整配置如下所示:
server:
port: 8000
spring:
profiles:
active: dev # 通过修改spring.profiles.active属性的值来选择不同环境的配置
include: feign # 引入application-feign.yml文件
application:
name: service-order
cloud:
nacos:
# 配置nacos地址,将微服务注册到nacos上
server-addr: 127.0.0.1:8848
config:
import-check:
enabled: false
# 如果没有配置spring.profiles.active,则默认使用public命名空间
namespace: ${spring.profiles.active:public}
sentinel:
transport:
# 配置sentinel地址,连接sentinel
dashboard: localhost:8080
eager: true # 饥饿加载,服务启动就连接sentinel,默认是第一次调用服务才会连接sentinel
web-context-unify: false # 是否统一web上下文,默认为true
datasource: # 数据源配置
flow-datasource: # 流控的数据源配置
nacos: # 当前的存储介质是 Nacos
server-addr: 127.0.0.1:8848 # nacos的地址
namespace: 677d9fd4-2dea-4391-891d-52671399ed48 # 命名空间id
group-id: SENTINEL_GROUP # 规则配置所属的组,一般建议大写
data-id: ${spring.application.name}_flow-rules # 新建配置指定的DATA ID
data-type: json # 配置格式,即配置数据内容的格式
rule-type: flow # sentinel规则的类型,flow表示流控规则
degrade-datasource: # 熔断降级的数据源配置
nacos: # 当前的存储介质是 Nacos
server-addr: 127.0.0.1:8848 # nacos的地址
namespace: 677d9fd4-2dea-4391-891d-52671399ed48 # 命名空间id
group-id: SENTINEL_GROUP # 规则配置所属的组,一般建议大写
data-id: ${spring.application.name}_degrade-rules # 新建配置指定的DATA ID
data-type: json # 配置格式,即配置数据内容的格式
rule-type: degrade # sentinel规则的类型,degrade表示熔断降级规则
logging:
level:
# 给com.springcloud.demo.order.feign包下的类设置日志级别为debug
com.springcloud.demo.order.feign: debug
---
spring:
config:
import:
- nacos:common.properties?group=service-order
- nacos:database.properties?group=service-order
activate:
on-profile: dev
---
spring:
config:
import:
- nacos:common.properties?group=service-order
activate:
on-profile: test
---
spring:
config:
import:
- nacos:database.properties?group=service-order
activate:
on-profile: product
5、先启动sentinel,再启动service-order服务,观察sentinel-dashboard控制台的规则是否与上面在nacos中配置的一致。


6、通过postman多次调用创建订单的接口,观察是否会触发sentinel限流。

7、修改nacos配置,观察sentinel-dashboard控制台是否也发生了变更。


nacos_config数据库的config_info表中的数据如下所示。

3、从Sentinel同步规则至Nacos
在Sentinel控制台修改的规则只是保存到内存中,如果应用服务重启,规则就会消失。若想要将Sentinel控制台创建或修改的规则持久化,就需要修改Sentinel-dashboard模块的源码,将其与指定的数据源(如Sentinel-Nacos数据源)进行对接,这样开发者通过Sentinel控制台所做出的任何流控配置就可以自动保存在数据源之中。Sentinel-dashboard源码中提供了与Nacos整合的流控规则(其他Sentinel规则都没有)持久化实现方式,本文就是通过修改官方提供的源代码来实现将Sentinel控制台创建或修改的流控规则持久化到Nacos上。
修改sentinel-dashboard模块的源码主要有以下几个步骤:
1. 下载sentinel的源码压缩包,如下图所示。

2. 解压之后使用idea打开sentinel整个项目工程,通过idea导入项目。
3. 修改sentinel-dashboard模块的application.properties文件,添加Nacos数据源的配置。

4. 修改sentinel-dashboard的pom.xml文件,去掉sentinel-datasource-nacos的scope。

5. 将test目录下的rule/nacos复制到src/main/java目录下的rule/nacos。

6. 修改NacosConfig类,添加在application.properties中配置的属性

package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.Properties;
/**
* @author Eric Zhao
* @since 1.4.0
*/
@Configuration
public class NacosConfig {
@Value("${nacos.address}")
private String address; // application.properties中配置的属性
@Value("${nacos.namespace}")
private String namespace;
@Value("${nacos.clusterName}")
private String clusterName;
@Bean
public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
return s -> JSON.parseArray(s, FlowRuleEntity.class);
}
@Bean
public ConfigService nacosConfigService() throws Exception {
// 进行Nacos服务的配置
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, address);
properties.put(PropertyKeyConst.NAMESPACE, namespace);
properties.put(PropertyKeyConst.CLUSTER_NAME, clusterName);
return ConfigFactory.createConfigService(properties);
}
}
7. 修改FlowControllerV2类,将"flowRuleDefaultProvider"修改成"flowRuleNacosProvider",将"flowRuleDefaultPublisher"修改成"flowRuleNacosPublisher"。

8. 将sentinel-dashboard\src\main\webapp\resources\app\scripts\directives\sidebar\sidebar.html文件中有关dashboard.flow的注释放开。这个放开是为了在sentinel-dashboard界面上可以看到流控规则V1 tab页。


9. 编译打包,生成sentinel-dashboard.jar包


10. 使用java -jar sentinel-dashboard.jar命令启动sentinel-dashboard,访问sentinel控制台,用户名和密码都是sentinel。

在流控规则V1 tab页可以看到之前在Nacos界面创建的流控规则。修改规则的阈值,观察Nacos界面是否也同步被修改了。如果在“流控规则V1” tab页看不到在Nacos界面创建的规则,可能的原因是创建规则时使用的GROUP_ID或DATA ID与sentinel-dashboard源码中的不一致,具体原因可查看本文最后的解释。


11. 修改之前Nacos界面上的流控规则count=20,修改之后变成了count=1.0,如下图所示。


另外,使用postman多次访问创建订单接口,触发了sentinel限流。

注意:sentinel-dashboard源码NacosConfigUtil中的GROUP_ID = "SENTINEL_GROUP",FLOW_DATA_ID_POSTFIX = "-flow-rules",在Nacos界面上创建流控规则时,使用的DATA ID必须是应用服务的名称-flow-rules,Group必须是SENTINEL_GROUP,否则在“流控规则V1”tab页看不到在Nacos上创建的流控规则。如果在Nacos界面创建流控规则时DATA ID后缀不是-flow-rules,或者GROUP_ID 不是 "SENTINEL_GROUP",要么修改Nacos的规则配置信息,要么修改sentinel-dashboard源码,使两者保持一致。



1130

被折叠的 条评论
为什么被折叠?



