diff --git a/lab-46/lab-46-sentinel-demo-file/pom.xml b/lab-46/lab-46-sentinel-demo-file/pom.xml new file mode 100644 index 00000000..264e529b --- /dev/null +++ b/lab-46/lab-46-sentinel-demo-file/pom.xml @@ -0,0 +1,54 @@ + + + + org.springframework.boot + spring-boot-starter-parent + 2.2.2.RELEASE + + + 4.0.0 + + lab-46-sentinel-demo-file + + + + + org.springframework.boot + spring-boot-starter-web + + + + + com.alibaba.csp + sentinel-core + 1.7.1 + + + + com.alibaba.csp + sentinel-transport-simple-http + 1.7.1 + + + + com.alibaba.csp + sentinel-spring-webmvc-adapter + 1.7.1 + + + + com.alibaba.csp + sentinel-parameter-flow-control + 1.7.1 + + + + com.alibaba.csp + sentinel-annotation-aspectj + 1.7.1 + + + + diff --git a/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/Application.java b/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/Application.java new file mode 100644 index 00000000..de707eb1 --- /dev/null +++ b/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/Application.java @@ -0,0 +1,17 @@ +package cn.iocoder.springboot.lab46.sentineldemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + // 设置系统属性 project.name,提供给 Sentinel 读取 + System.setProperty("project.name", "demo-application"); + + // 启动 Spring Boot 应用 + SpringApplication.run(Application.class, args); + } + +} diff --git a/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SentinelConfiguration.java b/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SentinelConfiguration.java new file mode 100644 index 00000000..7fabd27a --- /dev/null +++ b/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SentinelConfiguration.java @@ -0,0 +1,111 @@ +package cn.iocoder.springboot.lab46.sentineldemo.config; + +import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect; +import com.alibaba.csp.sentinel.datasource.Converter; +import com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource; +import com.alibaba.csp.sentinel.datasource.FileWritableDataSource; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; +import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +@Configuration +public class SentinelConfiguration { + + @Bean + public SentinelResourceAspect sentinelResourceAspect() { + return new SentinelResourceAspect(); + } + +// @Bean + public FileRefreshableDataSource> refreshableDataSource(ObjectMapper objectMapper) throws IOException { + // File 配置。这里先写死,推荐后面写到 application.yaml 配置文件中。 + ClassPathResource resource = new ClassPathResource("/flow-rule.json"); + + // 创建 FileRefreshableDataSource 对象 + FileRefreshableDataSource> refreshableDataSource = new FileRefreshableDataSource<>(resource.getFile(), + new Converter>() { // 转换器,将读取的文本配置,转换成 FlowRule 数组 + @Override + public List convert(String value) { + try { + return Arrays.asList(objectMapper.readValue(value, FlowRule[].class)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + }); + + // 注册到 FlowRuleManager 中 + FlowRuleManager.register2Property(refreshableDataSource.getProperty()); + return refreshableDataSource; + } + + @Bean + public FileWritableDataSource writableDataSource(ObjectMapper objectMapper) throws IOException { + // File 配置。这里先写死,推荐后面写到 application.yaml 配置文件中。 + String directory = System.getProperty("user.home") + File.separator + + "sentinel" + File.separator + + System.getProperty("project.name"); + mkdirs(directory); + String path = directory + File.separator + "flow-rule.json"; + creteFile(path); + + // 创建 FileRefreshableDataSource 对象 + FileRefreshableDataSource> refreshableDataSource = new FileRefreshableDataSource<>(path, + new Converter>() { // 转换器,将读取的文本配置,转换成 FlowRule 数组 + @Override + public List convert(String value) { + try { + return Arrays.asList(objectMapper.readValue(value, FlowRule[].class)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + }); + // 注册到 FlowRuleManager 中 + FlowRuleManager.register2Property(refreshableDataSource.getProperty()); + + // 创建 FileWritableDataSource 对象 + FileWritableDataSource> fileWritableDataSource = new FileWritableDataSource<>(path, + new Converter, String>() { + @Override + public String convert(List source) { + try { + return objectMapper.writeValueAsString(source); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + }); + + // 注册到 WritableDataSourceRegistry 中 + WritableDataSourceRegistry.registerFlowDataSource(fileWritableDataSource); + return fileWritableDataSource; + } + + private void mkdirs(String path) { + File file = new File(path); + if (file.exists()) { + return; + } + file.mkdirs(); + } + + private void creteFile(String path) throws IOException { + File file = new File(path); + if (file.exists()) { + return; + } + file.createNewFile(); + } + +} diff --git a/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SpringMvcConfiguration.java b/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SpringMvcConfiguration.java new file mode 100644 index 00000000..c6d06427 --- /dev/null +++ b/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SpringMvcConfiguration.java @@ -0,0 +1,53 @@ +package cn.iocoder.springboot.lab46.sentineldemo.config; + +import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor; +import com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebTotalInterceptor; +import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig; +import com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class SpringMvcConfiguration implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + // Add Sentinel interceptor +// addSentinelWebTotalInterceptor(registry); + addSentinelWebInterceptor(registry); + } + + private void addSentinelWebInterceptor(InterceptorRegistry registry) { + // 创建 SentinelWebMvcConfig 对象 + SentinelWebMvcConfig config = new SentinelWebMvcConfig(); + config.setHttpMethodSpecify(true); // 是否包含请求方法。即基于 URL 创建的资源,是否包含 Method。 + // config.setBlockExceptionHandler(new DefaultBlockExceptionHandler()); // 设置 BlockException 处理器。 +// config.setOriginParser(new RequestOriginParser() { // 设置请求来源解析器。用于黑白名单控制功能。 +// +// @Override +// public String parseOrigin(HttpServletRequest request) { +// // 从 Header 中,获得请求来源 +// String origin = request.getHeader("s-user"); +// // 如果为空,给一个默认的 +// if (StringUtils.isEmpty(origin)) { +// origin = "default"; +// } +// return origin; +// } +// +// }); + + // 添加 SentinelWebInterceptor 拦截器 + registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**"); + } + + private void addSentinelWebTotalInterceptor(InterceptorRegistry registry) { + // 创建 SentinelWebMvcTotalConfig 对象 + SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig(); + + // 添加 SentinelWebTotalInterceptor 拦截器 + registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns("/**"); + } + +} diff --git a/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/controller/DemoController.java b/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/controller/DemoController.java new file mode 100644 index 00000000..950b0916 --- /dev/null +++ b/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/controller/DemoController.java @@ -0,0 +1,82 @@ +package cn.iocoder.springboot.lab46.sentineldemo.controller; + +import com.alibaba.csp.sentinel.Entry; +import com.alibaba.csp.sentinel.SphU; +import com.alibaba.csp.sentinel.annotation.SentinelResource; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/demo") +public class DemoController { + + @GetMapping("/echo") + public String echo() { + return "echo"; + } + + @GetMapping("/test") + public String test() { + return "test"; + } + + @GetMapping("/sleep") + public String sleep() throws InterruptedException { + Thread.sleep(100L); + return "sleep"; + } + + // 测试热点参数限流 + @GetMapping("/product_info") + @SentinelResource("demo_product_info_hot") + public String productInfo(Integer id) { + return "商品编号:" + id; + } + + // 手动使用 Sentinel 客户端 API + @GetMapping("/entry_demo") + public String entryDemo() { + Entry entry = null; + try { + // 访问资源 + entry = SphU.entry("entry_demo"); + + // ... 执行业务逻辑 + + return "执行成功"; + } catch (BlockException ex) { + return "被拒绝"; + } finally { + // 释放资源 + if (entry != null) { + entry.exit(); + } + } + } + + // 测试 @SentinelResource 注解 + @GetMapping("/annotations_demo") + @SentinelResource(value = "annotations_demo_resource", + blockHandler = "blockHandler", + fallback = "fallback") + public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException { + if (id == null) { + throw new IllegalArgumentException("id 参数不允许为空"); + } + return "success..."; + } + + // BlockHandler 处理函数,参数最后多一个 BlockException,其余与原函数一致. + public String blockHandler(Integer id, BlockException ex) { + return "block:" + ex.getClass().getSimpleName(); + } + + // Fallback 处理函数,函数签名与原函数一致或加一个 Throwable 类型的参数. + public String fallback(Integer id, Throwable throwable) { + return "fallback:" + throwable.getMessage(); + } + +} diff --git a/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/web/GlobalExceptionHandler.java b/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/web/GlobalExceptionHandler.java new file mode 100644 index 00000000..21759025 --- /dev/null +++ b/lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/web/GlobalExceptionHandler.java @@ -0,0 +1,17 @@ +package cn.iocoder.springboot.lab46.sentineldemo.web; + +import com.alibaba.csp.sentinel.slots.block.BlockException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +@ControllerAdvice(basePackages = "cn.iocoder.springboot.lab46.sentineldemo.controller") +public class GlobalExceptionHandler { + + @ResponseBody + @ExceptionHandler(value = BlockException.class) + public String blockExceptionHandler(BlockException blockException) { + return "请求过于频繁"; + } + +} diff --git a/lab-46/lab-46-sentinel-demo-file/src/main/resources/flow-rule.json b/lab-46/lab-46-sentinel-demo-file/src/main/resources/flow-rule.json new file mode 100644 index 00000000..5994d16e --- /dev/null +++ b/lab-46/lab-46-sentinel-demo-file/src/main/resources/flow-rule.json @@ -0,0 +1,11 @@ +[ + { + "resource": "GET:/demo/echo", + "limitApp": "default", + "grade": 1, + "count": 5, + "strategy": 0, + "controlBehavior": 0, + "clusterMode": false + } +] diff --git a/lab-46/lab-46-sentinel-demo-file/src/main/resources/sentinel.properties b/lab-46/lab-46-sentinel-demo-file/src/main/resources/sentinel.properties new file mode 100644 index 00000000..be3ba228 --- /dev/null +++ b/lab-46/lab-46-sentinel-demo-file/src/main/resources/sentinel.properties @@ -0,0 +1 @@ +csp.sentinel.dashboard.server=127.0.0.1:7070 diff --git a/lab-46/lab-46-sentinel-demo-file/target/classes/sentinel.properties b/lab-46/lab-46-sentinel-demo-file/target/classes/sentinel.properties new file mode 100644 index 00000000..be3ba228 --- /dev/null +++ b/lab-46/lab-46-sentinel-demo-file/target/classes/sentinel.properties @@ -0,0 +1 @@ +csp.sentinel.dashboard.server=127.0.0.1:7070 diff --git a/lab-46/pom.xml b/lab-46/pom.xml index b20fd68f..2abba4a0 100644 --- a/lab-46/pom.xml +++ b/lab-46/pom.xml @@ -15,6 +15,7 @@ lab-46-sentinel-demo lab-46-sentinel-demo-nacos lab-46-sentinel-demo-apollo + lab-46-sentinel-demo-file