继续上一章未完成的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那么要失效...* 使用这个只需要在浏览器连续访问即可*/@PostConstructprivate 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 {@Autowiredprivate 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无返回值*/@Testvoid 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*/@Testvoid 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";}@PostConstructprivate 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*/@Testvoid 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*/@Testvoid 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";}@PostConstructprivate 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*/@Testvoid 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}”*/@Testvoid 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* }*/@PostConstructprivate void globleDegradeSentinelInitDegradeRules() {DegradeRule rule = new DegradeRule();rule.setResource("globel_degrade_sentinel");rule.setGrade(RuleConstant.DEGRADE_GRADE_RT); // // 基于响应时间的熔断rule.setCount(1000); // 响应时间超过 1000msrule.setTimeWindow(2); // 熔断时间窗口为 2 秒rule.setMinRequestAmount(2); // 默认值,统计窗口内的最小请求数rule.setStatIntervalMs(30000); // 默认值,统计窗口长度为 30000msDegradeRuleManager.loadRules(Collections.singletonList(rule));// 打印加载的规则log.info("Loaded degrade rules: {}", DegradeRuleManager.getRules());}
测试
/*** 案例四:熔断 --- 自定义全局异常处理类* 测试业务异常触发 熔断;* 正常情况,1、2会正常请求;3会熔断;4正常请求;5熔断;* 报错返回:CustomExceptionHandler/SentinelExceptionHandler里面的handlerDegradeException方法“{"msg":"被熔断","code":429}”;*/@Testvoid 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"* }* ]*/@PostConstructprivate 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); // 限制特定 IPAuthorityRuleManager.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次时限流*/@PostConstructprivate 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*/@Testvoid 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();}