SpringCloud Alibaba Sentinel 流量治理、熔断限流(四)
目录
一 概念引入
二 运作流程
三 使用
四 异常处理
1 拦截器全局兜底
2 指定资源方法兜底 blockHandler/fallbcak
3 远程调用的兜底 Fallback
4 sphu硬编码
五 几种常用的控制规则
1 流控规则
2 熔断规则
3 热点规则
4 授权规则
5 系统规则
一 概念引入
二 运作流程
三 使用
下载对应的jar包:
java -jar sentinel-dashboard-1.8.8.jar
Release v1.8.8 · alibaba/Sentinel
这里的默认端口是8080,最好与本地的启动端口进行区分
默认账号密码均为sentinel
在本地项目当中进行相对应的配置
spring.cloud.sentinel.transport.dashboard=localhost:8080
spring.cloud.sentinel.eager=true
重新启动项目展示:
四 异常处理
核心优先级总结
优先执行方法级别的兜底(
blockHandler
)。如果方法没有兜底,才会进入 全局 BlockExceptionHandler。
如果既没有
@SentinelResource
,也没有全局拦截器,那么异常会直接抛到调用方(用户就看到 500 错误页了 )。
三个层级:
1 拦截器全局兜底
代码定义:@SentinelResource注解
@SentinelResource("createOrder")@Overridepublic Order createOrder(Long productId, Long userId) {// Product product = getProductFromRemote3(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("张三");order.setAddress("青岛");// 远程调用获取商品信息order.setProductList(Arrays.asList(product));return order;}
控制台:进行限流
页面展示:限流结束
拦截器:自定义拦截器的返回规则
package com.ax.order.exception;import com.alibaba.csp.sentinel.adapter.spring.webmvc_v6x.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.ax.common.R;
import com.fasterxml.jackson.databind.ObjectMapper;
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();@Overridepublic void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,String s, BlockException e) throws Exception {httpServletResponse.setContentType("application/json;charset=utf-8");PrintWriter writer = httpServletResponse.getWriter();R error = R.error(500, s + "被Sentinel 给限流了" + e.getClass());String s1 = objectMapper.writeValueAsString(error);writer.write(s1);writer.flush();}
}
效果展示:
2 指定资源方法兜底 blockHandler/fallbcak
代码实现:
// sentinel有两种情况,正常则返回,异常则调用blockHandler指定的方法@SentinelResource(value = "createOrder", blockHandler = "createOrderFallback")@Overridepublic Order createOrder(Long productId, Long userId) {// Product product = getProductFromRemote3(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("张三");order.setAddress("青岛");// 远程调用获取商品信息order.setProductList(Arrays.asList(product));return order;}//兜底回调public Order createOrderFallback(Long productId, Long userId, BlockException e) {Order order = new Order();order.setId(1L);order.setTotalAmount(new BigDecimal(0));order.setUserId(userId);order.setNickName("未知用户");order.setAddress("异常信息" + e.getMessage());order.setProductList(null);return order;}
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.stereotype.Service;import java.math.BigDecimal;
import java.util.Arrays;@Service
public class OrderServiceImpl implements OrderService {@SentinelResource(value = "createOrder",blockHandler = "createOrderBlockHandler", // 限流、熔断时走这里fallback = "createOrderFallback" // 业务异常时走这里)@Overridepublic Order createOrder(Long productId, Long userId) {// 模拟远程调用商品服务Product product = productFeignClient.getProductById(productId);// 模拟异常(比如商品服务挂了)if (product == null) {throw new RuntimeException("商品不存在或服务不可用");}Order order = new Order();order.setId(1L);order.setTotalAmount(product.getPrice().multiply(new BigDecimal(product.getNum())));order.setUserId(userId);order.setNickName("张三");order.setAddress("青岛");order.setProductList(Arrays.asList(product));return order;}/*** blockHandler: 处理 Sentinel 限流/熔断/系统保护* 触发条件:流量超限、熔断等情况*/public Order createOrderBlockHandler(Long productId, Long userId, BlockException e) {Order order = new Order();order.setId(-1L);order.setTotalAmount(BigDecimal.ZERO);order.setUserId(userId);order.setNickName("系统繁忙");order.setAddress("触发流控/熔断:" + e.getClass().getSimpleName());order.setProductList(null);return order;}/*** fallback: 处理业务异常(运行时异常)* 触发条件:代码运行报错,如空指针、远程调用异常*/public Order createOrderFallback(Long productId, Long userId, Throwable e) {Order order = new Order();order.setId(-2L);order.setTotalAmount(BigDecimal.ZERO);order.setUserId(userId);order.setNickName("未知用户");order.setAddress("业务异常兜底:" + e.getMessage());order.setProductList(null);return order;}
}
1 如果是流量过大 / 系统保护(Sentinel 规则触发) → 用
blockHandler
例子:秒杀活动、热点参数限流。
作用:告诉用户“当前排队人数过多,请稍后再试”。
2 如果是业务逻辑异常 → 用
fallback
例子:远程调用下游服务失败(商品服务挂了)、数据处理出现异常。
作用:返回一个默认结果或缓存数据,保证系统可用性。
3 两者可以配合使用
blockHandler
→ 处理 限流/熔断。
fallback
→ 处理 业务异常。Sentinel 会优先调用
blockHandler
,如果没有配置,再进入fallback
。
3 远程调用的兜底 Fallback
package com.ax.order.feign;import com.ax.order.feign.fallback.ProductFeignClientFallback;
import com.ax.product.bean.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;@FeignClient(name = "service-product", fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {/*** 测试FeignClient** @param id*///mvc注解两套使用逻辑//标注在Controller上,为接收请求//标注在FeignClient上,为发送请求@GetMapping("/product/{id}")Product getProductById(@PathVariable("id") Long id);//如果调用自己其他服务的api直接将其方法复制过来即可,下面这个就是从product当中复制过来的// @GetMapping("/product/{id}")// Product getProduct(@PathVariable("id") Long id);
}
回调:
package com.ax.order.feign.fallback;import com.ax.order.feign.ProductFeignClient;
import com.ax.product.bean.Product;
import org.springframework.stereotype.Component;import java.math.BigDecimal;@Component
public class ProductFeignClientFallback implements ProductFeignClient {@Overridepublic Product getProductById(Long id) {System.out.println("兜底回调");Product product = new Product();product.setId(id);product.setProductName("商品不存在");product.setNum(0);product.setPrice(new BigDecimal("0.0"));return product;}
}
4 sphu硬编码
这个硬编码方式就是使用 SphU.entry()
来定义一个资源,配合 try-with-resources
或者 try-catch-finally
来做 限流、熔断、降级 控制。
五 几种常用的控制规则
1 流控规则
单机模式
流控规则:限制多余请求,从而保护系统资源不被耗尽。
资源名:标识限流规则的目标对象。
针对来源:看可以对不同来源设置不同的规则。
阈值类型:单机维度:QPS指的是秒请求数。并发线程数指的是秒请求线程数。
流控模式:触发条件
1 直接:对当前资源本身进行限流。2 关联:不仅受到当前资源的影响,如果当前资源下的调用消耗也大,将会将当前资源进行限流。3 链路:只对特定调用链路中的资源进行限流。
流控效果:超限之后如何处理
1 快速失败:直接返回异常 2 预热模式:防止项目的冷启动(项目启动后会慢慢的提升,不会直接到预定值,而是在指定时间内达到) 3 排队等待:超过阈值的进入等待队列,按照顺序依次排队执行。
关联:给readDb加流控,关联资源是writeDb,表面上是对readDb加流量管控,但是其大量请求并没有影响,影响的前提是writeDb的请求较大,那么就会将readDb进行限制
链路:(比如一个方法被多个地方引用,可以指定其具体的一个链路,比如说秒杀与退货,两个都有对数据库当中数据查询,那么就可以指定秒杀的链路进行流控)
集群模式
集群阈值模式:1 单机均摊:配置的阈值会按照机器数量平分到每台机器。2 总体阈值:阈值是所有的机器总和。
失败退化:当 Token Server 不可用时,是否自动退化为 单机限流。
上下文统一
web-context-unify: 主要用于 是否启用统一的 Web 上下文处理。
如果设置为
true
:
系统会对所有的 HTTP 响应做统一封装(比如统一响应格式、统一异常处理)。
方便前端统一处理接口返回数据和错误码。
如果设置为
false
:
不做统一封装,接口返回什么就原样返回。
适合需要原始返回结构或者第三方接口调用的场景
2 熔断规则
核心作用:熔断降级作为保护自身的手段,通常在客户端进行配置,便于更好的获得数据或者执行业务。
1 切断不稳定调用:通过持续监控下游服务的调用指标(如异常比例、响应时长),当故障超过预设阈值时,熔断器将自动开启,立即切断所有请求,防止问题蔓延。
2 快速返回不积压:熔断开启后,所有后续请求不再尝试访问故障服务,而是在本地即刻返回失败响应。此举能有效避免请求线程阻塞和资源堆积,保护调用方自身资源不被耗尽。
3 避免雪崩:将故障服务隔离在调用链之外,将其影响范围限制在局部,有效阻止了单个后端的故障向上蔓延,从而保障整个分布式系统的稳定性和高可用性,避免系统性崩溃。
断路器
工作原理
三个状态:关闭,半开,打开
这个状态机模型实现了一个全自动的、智能的故障隔离与恢复循环:
-
正常期:(Closed) 监控流量,一切正常。
-
故障检测:监控指标超标 -> 触发熔断 -> (Open) 快速失败,保护系统。
-
恢复试探:熔断时间到 -> (Half-Open) 放行探测请求。
-
恢复成功:探测成功 -> 回到 (Closed) 状态,系统恢复正常。
-
恢复失败:探测失败 -> 回到 (Open) 状态,继续熔断。
三个参数:慢调用比例,异常比例,异常数
慢调用比例:
最大RT:相当于响应时间,超过则为慢请求。1000ms
比例阈值:超过多少比例的请求为慢请求的条件。0-1
熔断时长:熔断时长。1s
最小请求数:达到一定的数量级才可以满足。
异常比例:
比例阈值:调用出现异常的比例。0-1
熔断时长:熔断时长。1s
最小请求数:达到一定数量级才可以满足。
异常数
异常数:出现异常的数量
熔断时长:熔断时长。1s
最小请求数:达到一定数量级才可以满足。
3 热点规则
热点规则:在原有的流控的基础之上,可以实现更加精细化的流量控制,不是对某个资源的一刀切,而是智能的识别出“热点参数”并对其实施特殊关照(限流操作,可以结合前面的兜底处理)。
展示:注意required=false的使用