服务容错治理框架resilience4j&sentinel基础应用---微服务的限流/熔断/降级解决方案

继续上一章未完成的sentinel;

直接实操;

关于测试:本文使用线程池线程异步执行模拟并发结合Mock框架测试

 其他文章

服务容错治理框架resilience4j&sentinel基础应用---微服务的限流/熔断/降级解决方案-CSDN博客

conda管理python环境-CSDN博客

快速搭建对象存储服务 - Minio,并解决临时地址暴露ip、短链接请求改变浏览器地址等问题-CSDN博客

大模型LLMs的MCP入门-CSDN博客

使用LangGraph构建多代理Agent、RAG-CSDN博客

大模型LLMs框架Langchain之链详解_langchain.llms.base.llm详解-CSDN博客

大模型LLMs基于Langchain+FAISS+Ollama/Deepseek/Qwen/OpenAI的RAG检索方法以及优化_faiss ollamaembeddings-CSDN博客

大模型LLM基于PEFT的LoRA微调详细步骤---第二篇:环境及其详细流程篇-CSDN博客

大模型LLM基于PEFT的LoRA微调详细步骤---第一篇:模型下载篇_vocab.json merges.txt资源文件下载-CSDN博客 使用docker-compose安装Redis的主从+哨兵模式_使用docker部署redis 一主一从一哨兵模式 csdn-CSDN博客

docker-compose安装canal并利用rabbitmq同步多个mysql数据_docker-compose canal-CSDN博客

目录

Step1、引入依赖

Step2、启动dashboard控制台

Step3、配置application.yml

Demo1、限流FlowRule---自定义局部异常拦截

controller

测试

Demo2、限流FlowRule---使用自定义统一处理异常类

controller

定义:处理异常类UnifiedSentinelHandler.java

测试

Demo3、限流FlowRule---自定义全局异常处理类

controller

定义全局的统一处理类-低级:GlobalExceptionHandler.java

定义全局的统一处理类-高级:SentinelExceptionHandler.java

测试

Demo4、熔断DegradeRule---自定义全局异常

controller

测试

Demo5、授权AuthorityRule --- 自定义全局异常

controller

测试

Demo6、热点参数ParamFlowRule-自定义全局异常

controller

测试


Step1、引入依赖

<!-- 版本控制 --> 
        <spring-boot.version>3.4.1</spring-boot.version>
        <spring-cloud.version>2024.0.0</spring-cloud.version>
        <spring-cloud-alibaba.version>2023.0.3.2</spring-cloud-alibaba.version>
<!-- 父项目依赖 --> 
            <!-- spring boot 依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- spring cloud 依赖 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- spring cloud alibaba 依赖 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

<!-- 子项目依赖 -->

        <!-- SpringCloud Alibaba Sentinel -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!-- 引入Springboot-web SpringMVC -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

Step2、启动dashboard控制台

需要去官网下载“sentinel-dashboard-1.8.8.jar”版本自行选择...

登录WebUI:http://localhost:8080/#/login

密码账号:sentinel/sentinel    --- 默认的

启动命令:
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

###CMD可能解析错误,所以用“""”转义
java "-Dserver.port=8080" "-Dcsp.sentinel.dashboard.server=localhost:8080" "-Dproject.name=sentinel-dashboard" -jar .\sentinel-dashboard-1.8.8.jar



在项目中配置:spring.cloud.sentinel.transport.dashboard=localhost:8080,就可以将项目绑定在sentinel-dashboard中;

Step3、配置application.yml

spring:
  profiles:
    active: @profiles.active@
    ### sentinel流量控制配置
    sentinel:
       .... 其他比如日志等配置略...
      eager: true # 取消控制台懒加载 即是否立即加载 Sentinel 规则
      transport:
        # 控制台地址 ... 目的是将这个注册到这个控制台,而不是在这个项目中打开控制台;
        # 如果要使用控制台,需要单独开启“sentinel-dashboard-1.8.8.jar”
        dashboard: 127.0.0.1:8080
#      filter:
#        enabled: false # 关闭 Web 层的自动资源名称生成
      web-context-unify: false # 打开调用链路

Demo1、限流FlowRule---自定义局部异常拦截

controller

@Slf4j
@RestController
@RequestMapping("/sentinel")
public class SentinelTestController {
/****************************************************************************************************************************/
    /**
     * 案例一:自定义局部异常拦截:
     * blockHandler:Sentinel 流量控制或熔断、降级触发时执行的回调方法;方法参数、返回值类型要和partSentinel()方法一致;
     * fallback:业务逻辑抛出异常时才会执行;方法参数、返回值类型要和partSentinel()方法一致..
     */
    @GetMapping("/part/{time}/{flag}")
    @SentinelResource(value = "part_sentinel", blockHandler = "partHandleBlock", fallback = "partFallbackMethod")
    public String partSentinel(@PathVariable("time") Long time, @PathVariable("flag") String flag) throws InterruptedException {
        if ("1".equals(flag)) {
            throw new NullPointerException("抛出异常...");
        }
        log.info("partSentinel 休眠...{}s", time);
        Thread.sleep(time * 1000);
        log.info("partSentinel 休眠结束...");
        return "partSentinel success";
    }

    private String partHandleBlock(Long time, String flag, BlockException ex) {
        return "熔断降级: " + ex.getClass().getSimpleName();
    }

    private String partFallbackMethod(Long time, String flag, Throwable ex) {
        return "方法执行异常: " + ex.getMessage();
    }

    /**
     * 如果使用了nacos那么要失效...
     * 使用这个只需要在浏览器连续访问即可
     */
    @PostConstruct
    private void partSentinelInitFlowRules() {
        FlowRule rule = new FlowRule();
        rule.setResource("part_sentinel");
        // FLOW_GRADE_QPS:基于QPS(每秒请求数)进行流量控制
        // FLOW_GRADE_THREAD:基于并发线程数进行流量控制
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//        rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);
        rule.setCount(1); // 每秒最多允许 1 个请求

        List<FlowRule> rules = new ArrayList<>();
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }
}

测试

使用CompletableFuture.runAsync异步无返回值+for循环+线程池+MockMvc形式测试

import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.hamcrest.Matchers.containsString;

@SpringBootTest
@AutoConfigureMockMvc
public class SentinelTestControllerMockTest {
    @Autowired
    private MockMvc mockMvc;
    static ThreadPoolExecutor threadPoolExecutor;

    static {
        /**
         * 5个核心线程、最多10个线程、5秒的线程空闲存活时间、能容纳100个任务的阻塞队列以及当任务无法添加到线程池时使用的策略;
         * 对于CPU密集型任务,你可能希望将核心线程数设置为与处理器数量相匹配;而对于IO密集型任务,则可以设置更高的线程数以提高并发度。
         */
        threadPoolExecutor = new ThreadPoolExecutor(
                5,
                10,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100),
                new ThreadPoolExecutor.CallerRunsPolicy());
    }
    
    /**
     * 测试流量控制(QPS = 1)
     * 连续发送两个请求,第二个会被限流
     * CompletableFuture.runAsync无返回值
     */
    @Test
    void testQpsFlowControlRunAsync() throws Exception {
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            final int index = i + 1;
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    String response = mockMvc.perform(get("/sentinel/part/1/0"))
                            .andReturn()
                            .getResponse()
                            .getContentAsString();
                    System.out.println("请求[" + index + "] 成功: " + response);
                } catch (Exception e) {
                    System.err.println("请求[" + index + "] 异常: " + e.getMessage());
                    throw new RuntimeException("请求失败: " + e.getMessage(), e);
                }
            }, threadPoolExecutor);
//            future.join(); // 等待异步线程执行完毕
            // 添加到列表用于后续统一处理
            futures.add(future);
        }

        // 等待所有请求完成
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0])
        );

        // 可选:添加最终聚合操作
        allFutures.thenRun(() -> System.out.println("✅ 所有异步请求已完成"));

        // 阻塞主线程直到全部完成(测试用)
        allFutures.join();
    }

    /**
     * 测试业务异常触发 fallback
     */
    @Test
    void testBusinessException() throws Exception {
//        mockMvc.perform(get("/sentinel/part/1/1"))
//                .andDo(MockMvcResultHandlers.print()) // 打印详细信息
//                .andExpect(status().isOk());
        System.out.println(mockMvc.perform(get("/sentinel/part/1/1")) // 打印返回值
                .andReturn()
                .getResponse()
                .getContentAsString());
//        mockMvc.perform(get("/sentinel/part/0/1"))
//                .andExpect(content().string("方法执行异常: 抛出异常..."));
    }
}

Demo2、限流FlowRule---使用自定义统一处理异常类

controller

    /**
     * 案例二:使用自定义统一处理异常类
     * 和案例一类似,方法参数、返回值保持一致;
     *
     * @param time
     * @param flag
     * @return
     * @throws InterruptedException
     */
    @GetMapping("/unified/{time}/{flag}")
    @SentinelResource(value = "unified_sentinel",
            blockHandlerClass = UnifiedSentinelHandler.class,
            blockHandler = "handleBlock1", // 指定熔断方法
            fallbackClass = UnifiedSentinelHandler.class,
            fallback = "fallbackMethod1")// 指定回调方
    public String unifiedSentinel(@PathVariable("time") Integer time, @PathVariable("flag") String flag) throws InterruptedException {
        if ("1".equals(flag)) {
            throw new NullPointerException("抛出异常...");
        }
        log.info("unifiedSentinel 休眠...{}s", time);
        Thread.sleep(time * 1000);
        log.info("unifiedSentinel 休眠结束...");
        return "unifiedSentinel success";
    }
    @PostConstruct
    private void unifiedSentinelInitFlowRules() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        rule.setResource("unified_sentinel");
        // FLOW_GRADE_QPS:基于QPS(每秒请求数)进行流量控制
        // FLOW_GRADE_THREAD:基于并发线程数进行流量控制
//        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);
        rule.setCount(1);
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
    }

定义:处理异常类UnifiedSentinelHandler.java

报错/限流熔断降级都会走这个类的返回值

public class UnifiedSentinelHandler {

    public static String handleBlock1(Integer time, String flag, BlockException ex) {
        /**
         * 限流了,会被抛出“FlowException”异常
         */
        return "SentinelHandler handleBlock1: " + ex.getClass().getSimpleName();
    }

    public static String fallbackMethod1(Long time, String flag, Throwable ex) {
        return "SentinelHandler fallbackMethod1: " + ex.getMessage();
    }
}

测试

    /**
     * 案例二:使用自定义统一处理异常类:
     * 直接使用线程池测试
     *
     * @throws Exception
     */
    @Test
    void testDemo2QpsFlowControlThreadPool() throws Exception {
        // 第一个请求应该成功,后续会被限流/降级
        for (int i = 0; i < 3; i++) {
            threadPoolExecutor.execute(() -> {
                try {
                    System.out.println(mockMvc.perform(get("/sentinel/unified/1/0"))
                            .andReturn()
                            .getResponse()
                            .getContentAsString());
                } catch (Exception e) {
                    e.printStackTrace(); // 明确打印异常
                }
            });
        }
        // 使用
        Thread.sleep(2000);
    }

    /**
     * 案例二:使用自定义统一处理异常类:
     * 测试业务异常触发 fallback
     */
    @Test
    void testDemo2BusinessException() throws Exception {
        System.out.println(mockMvc.perform(get("/sentinel/unified/1/1"))
                .andReturn()
                .getResponse()
                .getContentAsString());
    }

Demo3、限流FlowRule---自定义全局异常处理类

controller

  /**
     * 案例三:自定义全局异常处理类;
     * 结合@RestControllerAdvice处理即“GlobalExceptionHandler/SentinelExceptionHandler.java”类
     * Throwable ---> Exception ---> BlockException ---> FlowException/DegradeException/ParamFlowException/AuthorityException
     * 在异常处理时,系统会依次捕获:所以不能同时将Throwable/Exception和FlowException/DegradeException/ParamFlowException/AuthorityException设置在同一个类中;
     * 所以如果要使用通用的异常处理类,并且要拦截Exception/Throwable
     * 可以将BlockException系列异常和Exception/Throwable分开,并利用@Order注解设置优先级;
     * 此处定义两个类:GlobalExceptionHandler(处理全局)、SentinelExceptionHandler(处理Sentinel相关异常)
     * 测试,限流:FlowException.class类
     * @param time
     * @param flag
     * @return
     * @throws InterruptedException
     */
    @GetMapping("/globel/flow/{time}/{flag}")
    @SentinelResource(value = "globel_flow_sentinel")
    public String globleFlowSentinel(@PathVariable("time") Integer time, @PathVariable("flag") String flag) throws InterruptedException {
        if ("1".equals(flag)) {
            throw new RuntimeException("抛出异常...");
        }
        log.info("globleFlowSentinel 休眠...{}s", time);
        Thread.sleep(time * 1000);
        log.info("globleFlowSentinel 休眠结束...");
        return "globleFlowSentinel success";
    }

    @PostConstruct
    private void globleFlowSentinelInitFlowRules() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        rule.setResource("globel_flow_sentinel");
//        rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(1); // 每秒最多允许 1 个请求
        rules.add(rule);
        FlowRuleManager.loadRules(rules);
//        FlowRule{
//            resource=globel_flow_sentinel,
//                limitApp=default,
//                grade=0, // 0 表示线程数模式
//                count=1.0, // 限制并发线程数为 1
//                strategy=0, // 直接模式
//                controlBehavior=0, // 直接拒绝
//                warmUpPeriodSec=10, // 预热时间  ----- 有这个,所以启动项目以后,不能直接访问,要等几秒,限流才生效.
//                maxQueueingTimeMs=500 // 最大排队时间
//        }
        // 打印加载的规则
        log.info("Loaded flow rules: {}", FlowRuleManager.getRules());
    }

定义全局的统一处理类-低级:GlobalExceptionHandler.java

@RestControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE) // 最低优先级
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public Map<String, Object> handleException(Exception e) {
        log.error("捕获到Exception: ", e);
        return new HashMap<>() {{
            put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
            put("msg", "服务器内部错误");
        }};
    }

    @ExceptionHandler(Throwable.class)
    public Map<String, Object> handleThrowable(Throwable e) {
        log.error("捕获到Throwable: ", e);
        return new HashMap<>() {{
            put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
            put("msg", "系统异常");
        }};
    }
}

定义全局的统一处理类-高级:SentinelExceptionHandler.java


@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE) // 最高优先级
@Slf4j
public class SentinelExceptionHandler {

    @ExceptionHandler(FlowException.class)
    public Map<String, Object> handlerFlowException() {
        log.info("FlowException");
        return new HashMap<>() {{
            put("code", HttpStatus.TOO_MANY_REQUESTS.value());
            put("msg", "被限流");
        }};
    }

    @ExceptionHandler(DegradeException.class)
    public Map<String, Object> handlerDegradeException() {
        log.info("DegradeException");
        return new HashMap<>() {{
            put("code", HttpStatus.TOO_MANY_REQUESTS.value());
            put("msg", "被熔断");
        }};
    }

    @ExceptionHandler(ParamFlowException.class)
    public Map<String, Object> handlerParamFlowException() {
        log.info("ParamFlowException");
        return new HashMap<>() {{
            put("code", HttpStatus.TOO_MANY_REQUESTS.value());
            put("msg", "热点限流");
        }};
    }

    @ExceptionHandler(AuthorityException.class)
    public Map<String, Object> handlerAuthorityException() {
        log.info("AuthorityException");
        return new HashMap<>() {{
            put("code", HttpStatus.UNAUTHORIZED.value());
            put("msg", "暂无权限");
        }};
    }

    @ExceptionHandler(BlockException.class)
    public Map<String, Object> handleBlockException(BlockException e) {
        log.info("BlockException: {}", e.getClass().getSimpleName());
        return new HashMap<>() {{
            put("code", HttpStatus.TOO_MANY_REQUESTS.value());
            put("msg", "访问被限制");
        }};
    }
}

测试

 /**
     * 案例三:自定义全局异常处理类:
     * 直接使用线程池测试;
     * 限流返回:SentinelExceptionHandler里面的handlerFlowException方法“{"msg":"被限流","code":429}”
     *
     * @throws Exception
     */
    @Test
    void testDemo3QpsFlowControlThreadPool() throws Exception {
        // 第一个请求应该成功,后续会被限流/降级
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            final int index = i + 1;
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    String response = mockMvc.perform(get("/sentinel/globel/flow/5/0"))
                            .andReturn()
                            .getResponse()
                            .getContentAsString();
                    System.out.println("请求[" + index + "] 成功: " + response);
                } catch (Exception e) {
                    System.err.println("请求[" + index + "] 异常: " + e.getMessage());
                    throw new RuntimeException("请求失败: " + e.getMessage(), e);
                }
            }, threadPoolExecutor);
//            future.join(); // 等待异步线程执行完毕
            // 添加到列表用于后续统一处理
            futures.add(future);
        }

        // 等待所有请求完成
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0])
        );

        // 可选:添加最终聚合操作
        allFutures.thenRun(() -> System.out.println("✅ 所有异步请求已完成"));

        // 阻塞主线程直到全部完成(测试用)
        allFutures.join();
    }

    /**
     * 案例三:限流 --- 自定义全局异常处理类:
     * 测试业务异常触发 fallback;
     * 报错返回:GlobalExceptionHandler里面的handleException方法“{"msg":"服务器内部错误","code":500}”;
     * 如果没有设置Exception异常捕获那么会被handleFallback方法抛出“{"msg":"系统异常","code":500}”
     */
    @Test
    void testDemo3FlowException() throws Exception {
        System.out.println(mockMvc.perform(get("/sentinel/globel/flow/1/1"))
                .andReturn()
                .getResponse()
                .getContentAsString());
    }

Demo4、熔断DegradeRule---自定义全局异常

注意:完整的统一处理异常的类在demo3;

自定义全局;结合@RestControllerAdvice处理即“CustomExceptionHandler.java”类
* 测试熔断降级:DegradeException.class类

controller

    /**
     * 案例四:自定义全局;结合@RestControllerAdvice处理即“CustomExceptionHandler.java”类
     * 测试熔断降级:DegradeException.class类
     * <p>
     * 配置熔断规则方法一:在 Sentinel 控制台中,为资源 globelClass 添加降级规则,设置慢调用比例阈值(如响应时间超过 500ms 的比例超过 50%)
     * 配置熔断规则方法二:初始化DegradeRule
     *
     * 在sentinel中不支持超时;只能由客户端控制该请求的时间,服务端无法控制;
     *  - 比如:在本案例中系统会阻塞在"Thread.sleep(time * 1000)(工作中,可能是第三方服务、redis、MySQL网络等原因造成)",
     *  - 如果每个请求都要阻塞了50s,那么只有在第三个请求时才会自动熔断降级;
     *  - 这样一来只能等1、2请求完毕也就是100s以后才会熔断降级
     *  官方目前好像没有自动超时熔断的方法;
     *  解决方法一:所以我们可以使用CompletableFuture.supplyAsync异步请求,并设置超时返回值:
     *         // 使用异步任务执行核心逻辑
     *          CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
     *             try {
     *                 if ("1".equals(flag)) {
     *                     throw new RuntimeException("抛出异常...");
     *                 }
     *                 log.info("globleDegradeSentinel 休眠...{}s", time);
     *                 Thread.sleep(time * 1000);
     *                 log.info("globleDeggradeSentinel 休眠结束...");
     *                 return "globleDegradeSentinel success";
     *             } catch (InterruptedException e) {
     *                 Thread.currentThread().interrupt();
     *                 throw new RuntimeException("任务被中断", e);
     *             }
     *         });
     *
     *         // 设置超时时间(例如 3 秒)
     *         try {
     *             return future.get(3, TimeUnit.SECONDS);
     *         } catch (Exception e) {
     *             future.cancel(true); // 中断任务
     *             log.warn("接口超时,已中断");
     *             return "请求超时,请稍后重试";
     *         }
     *  其他解决方法: ---- 不过这个是这个方法调用第三方接口的---对于本例的sleep方法不适用。
     *      server.servlet.session.timeout=3
     *      server.tomcat.connection-timeout=3000
     *      spring.mvc.async.request-timeout=3000 # 设置SpringMVC的超时时间为5s
     */
    @GetMapping("/globel/degrade/{time}/{flag}")
    @SentinelResource(value = "globel_degrade_sentinel")
    public String globleDegradeSentinel(@PathVariable("time") Integer time, @PathVariable("flag") String flag) throws InterruptedException {
        if ("1".equals(flag)) {
            throw new RuntimeException("抛出异常...");
        }
        log.info("globleDegradeSentinel 休眠...{}s", time);
        Thread.sleep(time * 1000);
        log.info("globleDegradeSentinel 休眠结束...");
        return "globleDegradeSentinel success";
    }
    /**
     * 期望:在30s以内,有2个请求,接口响应时间超过 1000ms 时触发熔断,2s后恢复
     * 30s内请求2次:http://127.0.0.1:8077/weixin/sentinel/globel/degrade/5/10 被限制;哪怕是休眠时间结束了也会被限制;
     * 配置在nacos中的
     * {
     *         "resource": "globel_degrade_sentinel",
     *         "grade": 0,
     *         "count": 1000,
     *         "slowRatioThreshold": 0.1,
     *         "minRequestAmount":  2,
     *         "timeWindow": 2,
     *         "statIntervalMs": 30000
     *     }
     */
    @PostConstruct
    private void globleDegradeSentinelInitDegradeRules() {
        DegradeRule rule = new DegradeRule();
        rule.setResource("globel_degrade_sentinel");
        rule.setGrade(RuleConstant.DEGRADE_GRADE_RT); // // 基于响应时间的熔断
        rule.setCount(1000); // 响应时间超过 1000ms
        rule.setTimeWindow(2); // 熔断时间窗口为 2 秒
        rule.setMinRequestAmount(2);  // 默认值,统计窗口内的最小请求数
        rule.setStatIntervalMs(30000); // 默认值,统计窗口长度为 30000ms
        DegradeRuleManager.loadRules(Collections.singletonList(rule));
        // 打印加载的规则
        log.info("Loaded degrade rules: {}", DegradeRuleManager.getRules());
    }

测试

    /**
     * 案例四:熔断 --- 自定义全局异常处理类
     * 测试业务异常触发 熔断;
     * 正常情况,1、2会正常请求;3会熔断;4正常请求;5熔断;
     * 报错返回:CustomExceptionHandler/SentinelExceptionHandler里面的handlerDegradeException方法“{"msg":"被熔断","code":429}”;
     */
    @Test
    void testDemo4DegradeException() throws Exception {
        for (int i = 1; i < 6; i++) {
            LocalTime now = LocalTime.now();
            System.out.printf("当前时间:%02d:%02d | 请求次数:%02d | 返回值:%s%n",
                    now.getMinute(),
                    now.getSecond(),
                    i,
                    mockMvc.perform(get("/sentinel/globel/degrade/10/0"))
                            .andReturn()
                            .getResponse()
                            .getContentAsString());
            Thread.sleep(1000);
        }
    }

Demo5、授权AuthorityRule --- 自定义全局异常

controller

    /**
     * 案例五:自定义全局;结合@RestControllerAdvice处理即“CustomExceptionHandler/GlobalExceptionHandler/SentinelExceptionHandler”类
     * 测试授权:AuthorityException.class类
     * <p>
     * 方法一:在 Sentinel 控制台中,为资源 globelClass 添加授权规则,设置黑名单或白名单。
     * 方法二:初始化AuthorityRule
     *
     * @param time
     * @param flag
     * @return
     * @throws InterruptedException
     */
    @GetMapping("/globel/auth/{time}/{flag}")
    @SentinelResource(value = "globel_auth_sentinel")
    public String globleAuthSentinel(@PathVariable("time") Integer time, @PathVariable("flag") String flag, HttpServletRequest request) throws InterruptedException {
        if ("1".equals(flag)) {
            throw new RuntimeException("抛出异常...");
        }
//        if ("2".equals(flag)) {
//            globleAuthSentinelInitAuthRules(getClientIpAddress(request));
//        }
        System.out.println(getClientIpAddress(request));
        log.info("globleAuthSentinel 休眠...{}s", time);
        Thread.sleep(time * 1000);
        log.info("globleAuthSentinel 休眠结束...");
        return "globleAuthSentinel success";
    }

    private String getClientIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 如果经过多个代理,X-Forwarded-For 可能包含多个 IP,取第一个
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        return ip;
    }

    /**
     * 如果我们使用Nacos管理配置,就可以配置如下内容
     * 配置在nacos中的
     * [
     * {
     * "resource": "globel_auth_sentinel",
     * "strategy": 1,
     * "limitApp": "192.168.3.58,192.168.3.49"
     * }
     * ]
     */
    @PostConstruct
    private void globleAuthSentinelInitAuthRules() {
//        globleAuthSentinelInitAuthRules("192.168.3.58");
        globleAuthSentinelInitAuthRules("可以设置为手机IP");
    }

    private void globleAuthSentinelInitAuthRules(String ip) {
        AuthorityRule rule = new AuthorityRule();
        rule.setResource("globel_auth_sentinel");
//        rule.setStrategy(RuleConstant.AUTHORITY_WHITE); // 白名单
        rule.setStrategy(RuleConstant.AUTHORITY_BLACK); // 黑名单
        rule.setLimitApp(ip); // 限制特定 IP
        AuthorityRuleManager.loadRules(Collections.singletonList(rule));
        // 打印加载的规则
        log.info("Loaded auth rules: {}", AuthorityRuleManager.getRules());
    }

测试

关于测试和其他测试有所不同,在配置的时候可以使用手机来测试;

step1、设置“rule.setStrategy(RuleConstant.AUTHORITY_BLACK); // 黑名单”,并设置ip为“127.0.0.1” --- 此步限制本地IP连接---主要是为了使用手机能连接进来;

step2、查看开发电脑的IP,使用CMD命令控制台:ipconfig(window使用命令)

step3、找到“无线局域网适配器 WLAN:"--->“IPv4 地址 . . . . . . . . . . . . : xxxx”;

step4、用手机请求地址:“xxx/globel/auth/3/2” ---- 这一步主要是为了利用“System.out.println(getClientIpAddress(request));”获取手机ip

step5、在rule.setLimitApp(ip)设置IP,将其设置为手机IP;

最后效果:

设置rule.setStrategy=RuleConstant.AUTHORITY_BLACK ip=127.0.0.1;手机可访问,电脑访问不了;

设置rule.setStrategy=RuleConstant.AUTHORITY_BLACK ip=手机IP;其余设备可访问,手机访问不了;

设置rule.setStrategy=RuleConstant.AUTHORITY_WHITE ip=127.0.0.1;电脑可访问,其余设备访问不了;

设置rule.setStrategy=RuleConstant.AUTHORITY_WHITE ip=手机IP;其余设备不可访问,手机可以访问;

Demo6、热点参数ParamFlowRule-自定义全局异常

controller

    /**
     * 案例六:自定义全局;结合@RestControllerAdvice处理即“CustomExceptionHandler.java”类
     * 测试热点参数:ParamFlowException.class类
     * <p>
     * 方法一:在 Sentinel 控制台中,为资源 globelClass 添加热点参数规则,设置特定参数值的 QPS 阈值。
     * 方法二:初始化AuthorityRule
     */
    @GetMapping("/globel/paramflow/{time}/{flag}")
    @SentinelResource(value = "globel_param_flow_sentinel")
    public String globleParamFlowSentinel(@PathVariable("time") Integer time, @PathVariable("flag") String flag) throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        Long threadId = Thread.currentThread().getId();
        System.out.printf("threadName: %s | threadId: %d \n", threadName, threadId);
        if ("1".equals(flag)) {
            throw new NullPointerException("抛出异常...");
        }
        log.info("globleParamFlowSentinel 休眠...{}s", time);
        Thread.sleep(time * 1000);
        log.info("globleParamFlowSentinel 休眠结束...");
        return "globleParamFlowSentinel success";
    }

    /**
     * 对参数索引为“1”下标位置的参数进行限制,即对参数flag进行限制;
     * 期望一:限流模式为线程数模式,即在20s内,携带flag参数的请求超过3次(http://127.0.0.1:8077/sentinel/globel/paramflow/10/0),第4次将被限流;其他地址请求到第6次限流
     * 期望二:对 flag=10 时,请求到第二次进行限流;当“http://127.0.0.1:8077/weixin/sentinel/globel/paramflow/10/10”请求到第2次时被限流
     * 期望三:对 flag=测试 时,请求到第三次进行限流;即“http://127.0.0.1:8077/sentinel/globel/paramflow/10/测试”请求到第3次时限流
     */
    @PostConstruct
    private void globleParamFlowSentinelInitParamFlowRules() {
        // 当“http://127.0.0.1:8077/sentinel/globel/paramflow/8/2”请求到第4次时被限流
        ParamFlowRule rule = new ParamFlowRule("globel_param_flow_sentinel")
                .setParamIdx(1) // 参数索引(下标)(flag 参数) ;对应 SphU.entry(xxx, args) 中的参数索引位置
//                .setGrade(RuleConstant.FLOW_GRADE_THREAD) // 线程限流模式 
                .setGrade(RuleConstant.FLOW_GRADE_QPS) // QPS限流模式 
                .setDurationInSec(20) // 统计窗口时间 默认1s
                .setControlBehavior(0) // 流控制效果 匀速排队失败/快速失败(默认)
                .setMaxQueueingTimeMs(0) // 最大排队等待时间 ,仅在匀速排队模式生效
                .setCount(3); // 限流阈值

        ParamFlowItem item1 = new ParamFlowItem();
        item1.setCount(1); // 阈值
        item1.setObject("10"); // 参数值 ---- 对“.setParamIdx()”位置的参数的值进行限制; 本例是对“flag”的值进行限制;
        item1.setClassType(String.class.getName()); // 参数类型
        ParamFlowItem item2 = new ParamFlowItem();
        item2.setCount(2); // 阈值
        item2.setObject("测试"); // 参数值 --- 当参数为“测试”时,请求到第二次被限流
        item2.setClassType(String.class.getName()); // 参数类型

        List<ParamFlowItem> rules = new ArrayList<>();
        rules.add(item1);
        rules.add(item2);
        rule.setParamFlowItemList(rules);

        ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
        // 打印加载的规则
        log.info("Loaded ParamFlow rules: {}", ParamFlowRuleManager.getRules());
    }

测试

可以使用网页直接访问路径“/globel/paramflow/10/0”、“/globel/paramflow/10/10”、“/globel/paramflow/10/测试”;即可

使用Junit测试时

    /**
     * 案例六:热点参数
     * @throws Exception
     */
    @Test
    void testDemo6ParamFlowException() throws Exception {
        // 第一个请求应该成功,后续会被限流/降级
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            final int index = i + 1;
            CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
                try {
                    String response = mockMvc.perform(get("/sentinel/globel/paramflow/8/2"))
                            .andReturn()
                            .getResponse()
                            .getContentAsString();
                    System.out.println("请求[" + index + "] 成功,携带参数flag,第四次将被热点限流: " + response);
                } catch (Exception e) {
                    System.err.println("请求[" + index + "] 异常: " + e.getMessage());
                    throw new RuntimeException("请求失败: " + e.getMessage(), e);
                }
            }, threadPoolExecutor);
            CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
                try {
                    String response = mockMvc.perform(get("/sentinel/globel/paramflow/10/10"))
                            .andReturn()
                            .getResponse()
                            .getContentAsString();
                    System.out.println("请求[" + index + "] 成功,携带参数flag=10,第2次将被热点限流: " + response);
                } catch (Exception e) {
                    System.err.println("请求[" + index + "] 异常: " + e.getMessage());
                    throw new RuntimeException("请求失败: " + e.getMessage(), e);
                }
            }, threadPoolExecutor);
            CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> {
                try {
                    String response = mockMvc.perform(get("/sentinel/globel/paramflow/10/测试"))
                            .andReturn()
                            .getResponse()
                            .getContentAsString();
                    System.out.println("请求[" + index + "] 成功,携带参数flag=测试,第3次将被热点限流: " + response);
                } catch (Exception e) {
                    System.err.println("请求[" + index + "] 异常: " + e.getMessage());
                    throw new RuntimeException("请求失败: " + e.getMessage(), e);
                }
            }, threadPoolExecutor);
//            future.join(); // 等待异步线程执行完毕
            // 添加到列表用于后续统一处理
            futures.add(future1);
            futures.add(future2);
            futures.add(future3);
        }

        // 等待所有请求完成
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0])
        );

        // 可选:添加最终聚合操作
        allFutures.thenRun(() -> System.out.println("✅ 所有异步请求已完成"));

        // 阻塞主线程直到全部完成(测试用)
        allFutures.join();
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值