com.alibaba.csp
sentinel-core
1.8.4
@RestController
@Slf4j
public class SentinelController {
private static final String RESOURCE = "hello";
@GetMapping("/hello")
public String test(String name) {
try (Entry entry = SphU.entry(RESOURCE)){
log.info("hello {}", name);
return "hello " + name;
} catch (BlockException e) {
log.error("blocked by sentinel!", e);
return "blocked by sentinel!";
}
}
@PostConstruct
public void initFlowRules() {
List rules = new ArrayList<>();
FlowRule rule = new FlowRule();
//设置受保护的资源
rule.setResource(RESOURCE);
//设置流控规则
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
//设置受保护的资源阈值
rule.setCount(2);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
}
代码中一个方法时测试接口,一个是项目启动时给sentinel设置流控规则的初始化方法,该规则是给"hello"资源设置QPS为2。
注解的优势在于不侵入业务代码,非常友好。客户端引入依赖来支持注解的形式进行流控:
com.alibaba.csp
sentinel-annotation-aspectj
1.8.4
@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
业务代码通过@SentinelResource注解实现流控
@RestController
@Slf4j
public class SentinelAspectController {
private static final String RESOURCE = "hello2";
@SentinelResource(value = RESOURCE, blockHandler = "handleException", fallback = "fallbackException")
@GetMapping("/hello2")
public String test(String name) {
return "hello " + name;
}
public String handleException(String name, BlockException e) {
return "blocked by sentinel!";
}
public String fallbackException(String name, Throwable t) {
return "fallback by sentinel!";
}
}
注意引入了新资源必须在@PostConstrut初始化方法中新增新的流控规则:
FlowRule rule2 = new FlowRule();
//设置受保护的资源
rule2.setResource(RESOURCE2);
//设置流控规则
rule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
//设置受保护的资源阈值
rule2.setCount(2);
rules.add(rule2);
官方下载源码使用maven命令编译可生成sentinel-dashboard.jar包,通过java -jar可直接启动。
通过localhost:8080访问
客户端引入传输模块,将流控信息发送到sentinel控制台。
com.alibaba.csp
sentinel-transport-simple-http
1.8.4
客户端启动时,设置VM options参数:-Dcsp.sentinel.dashboard.server=localhost:8080指定sentinel服务端IP和port。再次访问hello2资源,客户端会将流控的请求和通过的请求等信息发送到sentinel控制台,实现秒级的实时监控。
dashboard中流控规则资源名如图(hello和hello2),它们是客户端通过http发送给sentinel的dashBoard服务端。也可以通过右上角的“新增流控规则”来新增需要被流控的资源,以及点击编辑来修改流控规则
工程目录如下,sentinel为根目录,spring-cloud包含client和server,client和server作为微服务测试工程。
在sentinel中引入父依赖和spring-cloud-alibaba的依赖管理
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>2.3.5.RELEASE</version>
<relativePath/>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.8.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
顺便了解下scope=import的含义:import只能在dependencyManagement定义,maven的工程只支持单继承,有时候为了打破这个规则,那么import就派上用场了,它可以让当前工程拥有父工程的所依赖的包。
spring-cloud工程包含四个子工程,只关注client和server。在spring-cloud的pom文件中加入spring-boot依赖管理和spring-cloud依赖管理,他们就是子工程公共的依赖包。
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- Spring Dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
spring.application.name=sentinel-test-client
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.username=nacos
spring.cloud.nacos.password=nacos
server.port=18088
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8080
#开启feign对sentinel的支持
feign.sentinel.enabled=true
spring.main.allow-bean-definition-overriding=true
spring.cloud.sentinel.web-context-unify=false
@RestController
@Slf4j
public class UserController {
@Resource
private UserFeign userFeign;
@GetMapping("/client/getUser")
public User getUser(@RequestParam String id, HttpServletRequest request) throws InterruptedException {
log.info("X-Request-color: {}", request.getHeader("X-Request-color"));
return userFeign.findUserById(id);
}
}
@FeignClient(value = "sentinel-test-server", fallback = UserFeignFallback.class, configuration = FeignConfiguration.class)
public interface UserFeign {
@GetMapping("/user/findUserById/{id}")
User findUserById(@PathVariable("id") String id);
}
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
spring.application.name=sentinel-test-server
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.username=nacos
spring.cloud.nacos.password=nacos
server.port=18087
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8080
@RestController
@Slf4j
public class UserController {
@GetMapping("/user/findUserById/{id}")
public User getUser(@PathVariable String id) throws InterruptedException {
User user = new User();
user.setAge("23");
user.setId(id);
user.setName("rick");
return user;
}
}
对客户端资源进行流控
在sentinel的dashboard新增flow流控规则,设置资源名为/client/getUser的PQS为2.
连续刷新http://localhost:18088/client/getUser?id=1,被流控了。
对服务端资源流控,删除客户端流控规则,新增服务端流控规则。
测试服务端流控效果,由于服务端返回的是字符串,所以client工程服务降级,返回feign的fallback,fallback定义的是空的user对象。
首先我们通过案例演示了sentinel的具体功能和效果。再来看它到底是什么,相对来讲更容易理解,sentinel相关文档可参考官方文档
参考官方文档:
sentinel的功能