Java研学-SpringCloud(十一)

六 熔断规则

1 介绍

  熔断降级一般用于远程调用,其核心目标在于通过
切断不稳定调用
(当下游服务性能下降或故障时主动拒绝请求)、
快速返回降级结果
(避免请求积压导致资源耗尽)以及
隔离故障防止雪崩效应
(阻止局部故障蔓延至整个系统),从而保障系统稳定性;

  其最佳实践是在客户端(调用端)进行配置,因为熔断降级是保护自己的,在该服务调用其他服务时,若其他服务存在问题,则切断连接进行快速返回,其核心组件为
断路器

2 断路器

  断路器模式通过模拟智能开关的
闭合-打开-半开状态
转换,为分布式系统提供动态容错保护:在依赖服务(如服务B)稳定时,断路器保持
闭合状态
,允许调用请求正常透传;

  当检测到服务B出现故障(如错误率超阈值、响应超时或异常堆积)时,断路器立即跳闸进入
打开状态
,此时所有调用被快速拦截并返回预设降级结果(如默认值或错误提示),此时调用请求并没有到达B,避免请求积压导致调用端资源耗尽或故障扩散;

  经过熔断时长后,断路器进入
半开状态
试探性恢复,仅放行少量请求检测服务B是否修复,若成功则恢复闭合状态,否则延长熔断时间,形成“监测-熔断-恢复”的闭环机制,有效平衡系统可用性与稳定性

3 断路器工作原理

  请求流量进入时,会设置统计时长和最小请求数,在统计时长内发送请求数达到最小请求数之上后,开始进行统计之后的流量情况是否触发异常

  慢调用比例:当慢请求超过设定的比例时,断路器状态变为打开。慢请求指:超过1秒(可设定的阈值)没有返回的请求
  异常比例:统计时长内返回结果异常居多,超过设定的比例
  异常数:统计时长内返回结果异常居多,超过设定的数额

  触发异常断路器转变状态为打开状态,会设置熔断时长(断路器处于打开状态的时间),断路器打开后,A不再发送请求给B,直接返回快速失败,熔断时间过后,断路器处于半开状态,

  断路器半开状态收到请求调用会先放行一个请求进行探测,成功则变为闭合状态,失败则变为打开状态,再次等待熔断时间结束后,转为半开状态,依次循环

4 设置熔断规则 – 慢调用比例

  为防止商品服务宕机从而对订单服务造成影响,可在订单中配置对商品的熔断规则

5 设置远程调用休眠时间并测试 – ProductController


@RestController
public class ProductController {
    @Autowired
    ProductService productService;

    // 根据ID获取商品数据
    @GetMapping("/product/{id}")
    public Product getProduct(@PathVariable("id") Long productId,
                              HttpServletRequest request) {
        // 获取请求头中 TokenA 的信息
        String tokenA = request.getHeader("TokenA");
        System.out.println("welcome TokenA:"+ tokenA);
        Product product=productService.getProductById(productId);
        // 休眠 2 秒
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return  product;
    }
}

  快速刷新并观察控制台,根据熔断规则前5个请求返回虽然慢但是有数据返回(休眠2秒),多次刷新触发熔断后,熔断时间内返回变快(不再调用,直接快速失败)
图片[1] - Java研学-SpringCloud(十一) - 宋马
图片[2] - Java研学-SpringCloud(十一) - 宋马
图片[3] - Java研学-SpringCloud(十一) - 宋马
图片[4] - Java研学-SpringCloud(十一) - 宋马
  经过熔断时间后,转为半开状态,可以再次发送请求,但此时只能通过少量请求,当请求成功且20秒内(半开等待期也叫冷却期,冷却期通常与熔断时间相同)无其他失败请求,恢复为闭合状态,若再次发送大量请求,则断路器再次转为打开状态,触发兜底回调
图片[5] - Java研学-SpringCloud(十一) - 宋马

6 设置熔断规则 – 异常比例

  ① 编写业务异常 – ProductController


@RestController
public class ProductController {
    @Autowired
    ProductService productService;

    // 根据ID获取商品数据
    @GetMapping("/product/{id}")
    public Product getProduct(@PathVariable("id") Long productId,
                              HttpServletRequest request) {
        // 获取请求头中 TokenA 的信息
        String tokenA = request.getHeader("TokenA");
        System.out.println("welcome TokenA:"+ tokenA);
        Product product=productService.getProductById(productId);
        // 异常
        int i=6/0;
        // 休眠 2 秒
//        try {
//            TimeUnit.SECONDS.sleep(2);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
        return  product;
    }
}

  ② 发送远程调用,返回状态码为500,触发兜底回调,此流程是将请求发送给远程后,远程产生异常返回,触发兜底回调(无熔断规则)
图片[6] - Java研学-SpringCloud(十一) - 宋马
图片[7] - Java研学-SpringCloud(十一) - 宋马
  ③ 有熔断规则后,当远程出现问题,熔断时间内,不会再发送请求到远程,直接兜底回调,节约资源
图片[8] - Java研学-SpringCloud(十一) - 宋马
  ④ 快速刷新触发熔断,订单控制台不再发送请求,直接进行兜底回调,商品服务不会接收到请求,业务异常没有请求进行触发,控制台无输出
图片[9] - Java研学-SpringCloud(十一) - 宋马

7 设置熔断规则 – 异常数

  ① 设置异常数
图片[10] - Java研学-SpringCloud(十一) - 宋马
  ② 测试
图片[11] - Java研学-SpringCloud(十一) - 宋马

七 热点规则

1 介绍

  热点规则是流量控制的一种特殊形式,它能够在资源级别流控的基础上,进一步针对资源调用时携带的特定参数进行精细化限流,从而更有效地防护高频热点数据对系统造成的冲击。

  热点规则用于统计和限制经常被访问的数据,例如商品 ID、用户 ID 等。通过热点规则,可以统计一段时间内某些参数的访问频次,并对这些高频访问的参数进行限流,以防止系统因局部热点而崩溃。

  在 Web 应用中,热点规则需要埋点是因为 Web 请求的多样性和动态性导致参数位置、类型不固定,而 Sentinel 需通过埋点显式绑定资源名、精准提取并标准化监控参数,才能基于“资源+参数值”实现高频热点数据的精细化限流。

2 进行自定义埋点测试

  ① 取消之前设置的异常 – ProductController


@RestController
public class ProductController {
    @Autowired
    ProductService productService;

    // 根据ID获取商品数据
    @GetMapping("/product/{id}")
    public Product getProduct(@PathVariable("id") Long productId,
                              HttpServletRequest request) {
        // 获取请求头中 TokenA 的信息
        String tokenA = request.getHeader("TokenA");
        System.out.println("welcome TokenA:"+ tokenA);
        Product product=productService.getProductById(productId);
        // 业务异常
//        int i=6/0;
        // 休眠 2 秒
//        try {
//            TimeUnit.SECONDS.sleep(2);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
        return  product;
    }
}

  ② 进行埋点 – OrderController


@Slf4j
@RestController
public class OrderController {
    @Autowired
    OrderService orderService;

    // 所需属性
    @Autowired
    OrderProperties orderProperties;

    @GetMapping("/config")
    public String config(){
        return "order.timeout:"+orderProperties.getTimeOut()
                + ";order.autoconfirm:"+orderProperties.getAutoConfirm()
                + ";order.dbUrl:"+orderProperties.getDbUrl();
    }

    @GetMapping("/create")
    public Order createOrder(@RequestParam("productId") Long productId,
                             @RequestParam("userId") Long userId) {
        Order order = orderService.createOrder(productId, userId);
        return order;
    }

    // 自定义资源埋点并设置兜底回调方法
    // fallback:处理业务异常(回调方法需要参数Throwable)。
	// blockHandler:处理流控异常(回调方法需要参数BlockException)。
    @GetMapping("/createB")
    @SentinelResource(value = "createOrderB-Order",fallback = "createOrderBFallback")
    public Order createOrderB(@RequestParam("productId") Long productId,
                             @RequestParam("userId") Long userId) {
        Order order = orderService.createOrder(productId, userId);
        order.setId(9999l);
        return order;
    }
    // 兜底回调方法 
    public Order createOrderBFallback(Long productId, Long userId, Throwable exception) {
        System.out.println("兜底回调啦");
        // 无需创建订单 返回兜底订单数据即可
        Order order = new Order();
        order.setId(productId);
        order.setUserId(userId);
        order.setNickname("某人");
        order.setAddress("异常信息"+exception.getClass());
        return order;
    }

    @GetMapping("/dbInsert")
    public String insert() {
        return "Insert OK";
    }

    @GetMapping("/dbQuery")
    public String query() {
        log.info("寻寻觅觅");
        return "Query OK";
    }
}

  ③ 设置热点参数规则,
createOrderB
方法会传递2个参数,参数索引
0
,对应第
1
个参数
图片[12] - Java研学-SpringCloud(十一) - 宋马
  ④ 快速刷新测试,出现流控异常,无论商品ID为何值
图片[13] - Java研学-SpringCloud(十一) - 宋马
图片[14] - Java研学-SpringCloud(十一) - 宋马
  ⑤ 更新方法,为该参数设置默认值 – OrderController


    // 自定义资源埋点并设置兜底回调方法 设置方法参数自带默认值 从而不在路径中传递
    @GetMapping("/createB")
    @SentinelResource(value = "createOrderB-Order",fallback = "createOrderBFallback")
    public Order createOrderB(@RequestParam(value = "productId",defaultValue = "4399") Long productId,
                             @RequestParam(value = "userId",defaultValue = "3366") Long userId) {
        Order order = orderService.createOrder(productId, userId);
        order.setId(9999l);
        return order;
    }

  ⑥ 设置规则(规则同上),进行测试,发现由于默认值存在,仍会进行流控
图片[15] - Java研学-SpringCloud(十一) - 宋马
  ⑦ 更新方法,该参数可以不带 – OrderController


    // 自定义资源埋点并设置兜底回调方法 设置方法参数自带默认值 从而不在路径中传递
    @GetMapping("/createB")
    @SentinelResource(value = "createOrderB-Order",fallback = "createOrderBFallback")
    public Order createOrderB(@RequestParam(value = "productId",required = false) Long productId,
                             @RequestParam(value = "userId",required = false) Long userId) {
        Order order = orderService.createOrder(productId, userId);
        order.setId(9999l);
        return order;
    }

  ⑧ 设置规则(规则同上),进行测试,发现由于没有携带该参数,不会进行流控

3 高级选项 – 设置例外项

  ① 设置规则
图片[16] - Java研学-SpringCloud(十一) - 宋马
图片[17] - Java研学-SpringCloud(十一) - 宋马
  ② 测试,此时商品id为666的订单不会被限流,其他的id会被限流
图片[18] - Java研学-SpringCloud(十一) - 宋马

4 第二个参数设置 – 针对ID不可访问

  ① 建立规则,第二个参数需要再建立一个规则
图片[19] - Java研学-SpringCloud(十一) - 宋马
  ② 设立针对ID,该ID不可访问
图片[20] - Java研学-SpringCloud(十一) - 宋马
  ③ 测试发现,用户id为365则不可访问,即使另一个参数为例外项也不行
图片[21] - Java研学-SpringCloud(十一) - 宋马

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容