Cyan Blog Cyan Blog
首页
  • Java (opens new window)
  • JUC (opens new window)
  • JVM (opens new window)
  • Redis

    • Redis安装 (opens new window)
    • Redis基础 (opens new window)
    • Redis实战 (opens new window)
    • Redis集群安装 (opens new window)
    • Redis分布式缓存 (opens new window)
    • Redis多级缓存 (opens new window)
    • Redis原理 (opens new window)
  • 管理工具

    • Maven (opens new window)
    • Git (opens new window)
  • SSM

    • Spring (opens new window)
    • SpringBoot (opens new window)
    • Mybatis (opens new window)
    • MybatisPlus (opens new window)
  • 微服务

    • Docker (opens new window)
    • RabbitMQ (opens new window)
    • SpringCloud (opens new window)
    • Dubbo (opens new window)
    • MongoDB (opens new window)
    • Zookeeper (opens new window)
  • Java面试题 (opens new window)
  • JUC面试题 (opens new window)
  • JVM面试题 (opens new window)
  • Linux面试题 (opens new window)
  • SQL面试题 (opens new window)
  • Maven面试题 (opens new window)
  • Redis面试题 (opens new window)
  • SSM面试题 (opens new window)
  • SpringCloud面试题 (opens new window)
  • Linux (opens new window)
  • C++ (opens new window)
  • 数据库

    • MySQL (opens new window)
    • NoSQL (opens new window)
  • 软件测试

    • 软件测试 (opens new window)
  • 加密解密 (opens new window)
  • bilibili字幕提取 (opens new window)
  • 道理 (opens new window)
  • 关于博主

    • Github (opens new window)
    • CSDN (opens new window)
  • 关于本站

    • 如何搭建博客网站 (opens new window)
首页
  • Java (opens new window)
  • JUC (opens new window)
  • JVM (opens new window)
  • Redis

    • Redis安装 (opens new window)
    • Redis基础 (opens new window)
    • Redis实战 (opens new window)
    • Redis集群安装 (opens new window)
    • Redis分布式缓存 (opens new window)
    • Redis多级缓存 (opens new window)
    • Redis原理 (opens new window)
  • 管理工具

    • Maven (opens new window)
    • Git (opens new window)
  • SSM

    • Spring (opens new window)
    • SpringBoot (opens new window)
    • Mybatis (opens new window)
    • MybatisPlus (opens new window)
  • 微服务

    • Docker (opens new window)
    • RabbitMQ (opens new window)
    • SpringCloud (opens new window)
    • Dubbo (opens new window)
    • MongoDB (opens new window)
    • Zookeeper (opens new window)
  • Java面试题 (opens new window)
  • JUC面试题 (opens new window)
  • JVM面试题 (opens new window)
  • Linux面试题 (opens new window)
  • SQL面试题 (opens new window)
  • Maven面试题 (opens new window)
  • Redis面试题 (opens new window)
  • SSM面试题 (opens new window)
  • SpringCloud面试题 (opens new window)
  • Linux (opens new window)
  • C++ (opens new window)
  • 数据库

    • MySQL (opens new window)
    • NoSQL (opens new window)
  • 软件测试

    • 软件测试 (opens new window)
  • 加密解密 (opens new window)
  • bilibili字幕提取 (opens new window)
  • 道理 (opens new window)
  • 关于博主

    • Github (opens new window)
    • CSDN (opens new window)
  • 关于本站

    • 如何搭建博客网站 (opens new window)
  • SSM

  • SpringBoot

  • SpringCloud

    • SpringCloud
    • Nacos
    • OpenFeign
    • Sentinel
      • 流量保护
      • 环境搭建
      • 异常处理
      • 规则
      • fallback与blockHandler兜底回调
      • 总结
    • Gateway
    • Seata——分布式事务
  • Docker
  • Dubbo
  • MongoDB
  • Zookeeper
  • Spring生态
  • SpringCloud
2025-05-17
0
0
目录

Sentinel

# 流量保护

官⽹:https://sentinelguard.io/zh-cn/index.html

wiki:https://github.com/alibaba/Sentinel/wiki

Sentinel 具有以下特征:

丰富的应用场景:Sentinel 承接了阿⾥巴巴近 10 年的双⼗⼀⼤促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。

完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。

⼴泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 SpringCloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。

完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

image-20250525151801775

image-20250525151938294

# 资源&规则

定义资源:

  • 主流框架⾃动适配(Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux、Reactor);所有Web接口均为资源
  • 编程式:SphU API
  • 声明式:@SentinelResource

定义规则:

  • 流量控制规则
  • 熔断降级规则
  • 系统保护规则
  • 来源访问控制规则
  • 热点参数规则

image-20250525152948030

# 环境搭建

# 依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

# 启动控制台

java -jar sentinel.jar

# 配置连接(懒加载)

spring:
    cloud:
        sentinel:
            transport:
            	dashboard: localhost:8080
            eager: true

默认是懒加载,只有访问时才会在控制台显示资源,可以通过eager指定为启动即加载

使用@SentinelResource注解表示监控该资源

# 异常处理

一个规则对应一个异常

  • 流量控制规则
  • 熔断降级规则
  • 系统保护规则
  • 来源访问控制规则
  • 热点参数规则

image-20250525161739260

# Web接口

当Web接口(Controller接口)访问被拒绝(因为资源控制)后会在BlockExceptionHandler返回异常

# 自定义 BlockExceptionHandler

@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    public void handle(HttpServletRequest  esponse response, String resourceName, BlockException e) throws Exception {
        response.setStatus(429); //too many requests
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        R error = R.error(500, resourceName + " 被Sentinel限制了,原因:" +
                e.getClass());
        String json = objectMapper.writeValueAsString(error);
        writer.write(json);
        writer.flush();
        writer.close();
    }
}

由于当前的规则存储在内存中,每次重启都需要重新配置流量控制规则

# @SentinelResource

# blockHandler

@SentinelResource(value = "createOrder",blockHandler = "createOrderFallback")
@Override
public Order createOrder(Long productId, Long userId) {
// Product product = getProductFromRemoteWithLoadBalanceAnnotation(productId);
//使用Feign完成远程调用
    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(Arrays.asList(product));
    return order;
}
//兜底回调
public Order createOrderFallback(Long productId, Long userId, BlockException e){
    Order order = new Order();
    order.setId(0L);
    order.setTotalAmount(new BigDecimal("0"));
    order.setUserId(userId);
    order.setNickName("未知用户");
    order.setAddress("异常信息:"+e.getClass());
    return order;
}

@SentinelResource需要通过blockHandler指定一个兜底回调,在兜底回调中使用BlockException参数捕获并处理异常,不指定会将异常向上抛,也可以在SpringBoot全局异常处理器(@RestControllerAdvice)中处理异常

# openFeign——兜底回调

@FeignClient(value = "service-product",fallback = ProductFeignClientFallback.class) // feign客户端
public interface ProductFeignClient {
    //mvc注解的两套使用逻辑
    //1、标注在Controller上,是接受这样的请求
    //2、标注在FeignClient上,是发送这样的请求
    @GetMapping("/product/{id}")
    Product getProductById(@PathVariable("id") Long id);
}
@Component
public class ProductFeignClientFallback implements ProductFeignClient {
    @Override
    public Product getProductById(Long id) {
        System.out.println("兜底回调....");
        Product product = new Product();
        product.setId(id);
        product.setPrice(new BigDecimal("0"));
        product.setProductName("未知商品");
        product.setNum(0);
        return product;
    }
}

请求失败会触发兜底回调ProductFeignClientFallback,如果没有会将异常向上抛出到springboot全局异常处理器处理

# SphU硬编码

try{
	Entry entry = SphU.entry("haha");
}catch(BlockException e){

}

获取资源haha,获取失败会抛出异常BlockException

# 总结

在sentinel控制台中配置好规则后,违反规则就会调用我们编写的异常处理回调,如果没有则调用全局异常处理器

# 规则

# 流控规则——流量控制

Sentinel控制台中显示的资源名包括:

  • 自动检测到的Web路径请求

  • 使用@SentinelResource指定的资源名称

  • 远程调用的完整请求描述

# 阈值类型

  • QPS:使用计数器来统计通过的请求数量,轻量级,性能好
  • 并发线程数:底层使用线程池来控制线程数,引入了线程池,性能不好,适用于控制线程数量情况

# 集群阈值模式

  • 单机均摊:设置每个机器都是同一个阈值
  • 总体阈值:设置所有的机器加起来的阈值

# 流控模式

image-20250528094931158

直接策略:直接控制资源A的访问量

链路策略:控制B通过链路C的访问量,通过资源A不限制,通过资源B限制

  • 通过sentinel.web-context-unify: false 设置不统一web上下文,分割请求链路,才能对不同链路进行不同的资源请求限制

关联策略:根据写的访问量控制读的访问量,写多时限制读

# 流控效果

image-20250528102710695
  • 直接拒绝
  • Warm Up:period预热时间,一开始只启动1/3的QPS,period时间内提升到所有的QPS
  • 匀速排队:超过Timeout没有排队成功会被丢弃

使用Warm Up、匀速排队后流控模式会被忽略 即只有直接策略能生效

# 熔断规则——熔断降级

当被调用不稳定时,为了避免因为积压等待导致服务雪崩,,会认为被调用方不可用,快速切断联系,不去调用

熔断降级一般在调用方设置

# 断路器

image-20250528104931711

# 工作原理

image-20250528105321118

断路器默认是关闭状态,设置一个慢调用比例,当统计时长statIntervalMs内达到最小请求数minRequestAmount时进行统计异常数(超时没有返回结果)当异常比例达到慢调用比例70%时会认为对方不稳定,将断路器打开,经过熔断时长timeWindow后会进入半开状态(会放行一个请求进行探测),如果请求调用成功,则断路器回复关闭状态,否则保持打开直到探测成功

# 熔断策略

  • 慢调用比例

    image-20250528125521231

    分别设置

    • 返回时间RT、慢调用比例、熔断时长timeWindow、最小请求数minRequestAmount、统计时长statIntervalMs
  • 异常比例

    image-20250528131301540
    • 当远程调用出现异常时会有兜底回调,虽然每次调用都能兜底回调,但是会影响系统性能,使用熔断策略异常比例可以在异常多时熔断 提高系统响应速度
    • image-20250528131722954
  • 异常数:统计时长内出现一定的异常数会触发熔断

    image-20250528134530171

# 热点规则——热点参数

image-20250528143936838

使用热点参数可以满足以下需求

  1. 每个用户秒杀下单QPS不能超过1
  2. vip用户不限制
  3. 666号 是下架商品,不能访问

注意:目前Sentinel自带的adapter仅Dubbo方法埋点带了热点参数,其它适配模块(如Web)默认不支持热点规则,可通过自定义埋点方式指定新的资源名并传入希望的参数。注意自定义埋点的资源名不要和适配模块生成的资源名重复,否则会导致重复统计,

如

@GetMapping("/seckill")
@SentinelResource(value = "seckill-order", fallback = "seckillFallback")
public Order seckill(@RequestParam("userId") Long userId,
                     @RequestParan("productId") Long productId) {
    Order = orderService.createorder(productId, userId);
    order.setId(Long.MAX_VALUE);
    return order;
}

public Order seckillFallback(Long userId, Long productId, BlockException exception) {
    System.out.println("seckillFallback...");
    Order order = new Order();
    order.setId(productId);
    order.setuserId(userId);
    order.setAddress("异常信息:" + exception.getclass()):
    return order;
}
  • GetMapping不支持热点规则,需要自定义埋点@SentinelResource,资源名不要重复seckill,设置为seckill-order

  • 如果设置Required=false可以不携带参数,则不携带参数时不会被流控

# 设置热点规则

image-20250528154728618
  • 索引从0开始,设置每个用户id(索引0)每秒只能通过1个请求
  • 设置例外项:vip不限制访问

再添加一个热点规则

image-20250528155113229
  • 设置可以访问商品id(索引1)
  • 禁止访问666号商品

# fallback与blockHandler兜底回调

@GetMapping("/seckill")
@SentinelResource(valve = "seckill-order", fallback = "seckillFallback")
public Order seckill(@RequestParam(value = "userId", required = false) Long userId,
                     @RequestParam(value = "productId", defaultValve = "1000") Long productId) {
    Order order = orderService.createOrder(productId, userId);
    order.setId(Long.MAX_VALUE);
    return order;
}

在测试热点限流的时候,在这个方法里边,虽然我们写了fallback兜底回调,但是没有被调用,而是返回了默认的错误页,原因在于兜底回调有两种写法,blockHandler和fallback。 而@SentinelResource注解,处理异常的规则是有blockHandler优先使用blockHandler,没有指定blockHandler才走fallback, 也就是没有调用我们写的fallback处理逻辑,但是如果我们把fallback改为blockHandler是可以的正常返回,

blockHandler可以专门处理被流控的异常,而fallback比block的最大好处,就是它还能处理业务异常,业务异常不属于blockException,我们只需要把fallback回调方法的签名blockException改为Throwable,public Order seckillFallback(Long userId,Long productId,BlockException->Throwable exception){}就可以处理业务异常

# 总结

配置的规则都是存储在内存中的,可以结合nacos配置在nacos中,nacos连接数据库实现持久化

流控规则、熔断规则、兜底回调blockHandler、fallback

上次更新: 2025/6/10 17:21:17
OpenFeign
Gateway

← OpenFeign Gateway→

最近更新
01
md
06-10
02
Redis
06-10
03
HBase
06-10
更多文章>
Theme by Vdoing | Copyright © 2025-2025 Cyan Blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式