sentinel 使用 file 作为数据源示例

This commit is contained in:
YunaiV
2020-01-31 16:56:28 +08:00
parent f96c656852
commit ae3b9db52c
10 changed files with 348 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>lab-46-sentinel-demo-file</artifactId>
<dependencies>
<!-- 实现对 SpringMVC 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Sentinel 核心库 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.7.1</version>
</dependency>
<!-- Sentinel 接入控制台 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.7.1</version>
</dependency>
<!-- Sentinel 对 SpringMVC 的支持 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-webmvc-adapter</artifactId>
<version>1.7.1</version>
</dependency>
<!-- Sentinel 对【热点参数限流】的支持 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
<version>1.7.1</version>
</dependency>
<!-- Sentinel 对 Spring AOP 的拓展 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.7.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -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);
}
}

View File

@@ -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<List<FlowRule>> refreshableDataSource(ObjectMapper objectMapper) throws IOException {
// File 配置。这里先写死,推荐后面写到 application.yaml 配置文件中。
ClassPathResource resource = new ClassPathResource("/flow-rule.json");
// 创建 FileRefreshableDataSource 对象
FileRefreshableDataSource<List<FlowRule>> refreshableDataSource = new FileRefreshableDataSource<>(resource.getFile(),
new Converter<String, List<FlowRule>>() { // 转换器,将读取的文本配置,转换成 FlowRule 数组
@Override
public List<FlowRule> 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<List<FlowRule>> refreshableDataSource = new FileRefreshableDataSource<>(path,
new Converter<String, List<FlowRule>>() { // 转换器,将读取的文本配置,转换成 FlowRule 数组
@Override
public List<FlowRule> 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<List<FlowRule>> fileWritableDataSource = new FileWritableDataSource<>(path,
new Converter<List<FlowRule>, String>() {
@Override
public String convert(List<FlowRule> 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();
}
}

View File

@@ -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("/**");
}
}

View File

@@ -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();
}
}

View File

@@ -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 "请求过于频繁";
}
}

View File

@@ -0,0 +1,11 @@
[
{
"resource": "GET:/demo/echo",
"limitApp": "default",
"grade": 1,
"count": 5,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]

View File

@@ -0,0 +1 @@
csp.sentinel.dashboard.server=127.0.0.1:7070

View File

@@ -0,0 +1 @@
csp.sentinel.dashboard.server=127.0.0.1:7070

View File

@@ -15,6 +15,7 @@
<module>lab-46-sentinel-demo</module>
<module>lab-46-sentinel-demo-nacos</module>
<module>lab-46-sentinel-demo-apollo</module>
<module>lab-46-sentinel-demo-file</module>
</modules>