sentinel入门

面向分布式、多语言异构化服务架构的流量治理组件

https://sentinelguard.io/zh-cn/

1 常见的容错方案

就以B调用C服务为例,怎样避免B服务因为C服务的不可用而导致自己不可用

  1. 超时

  1. 限流

  2. 舱壁模式

  1. 断路器

    https://martinfowler.com/bliki/CircuitBreaker.html

2 快速入门

https://sentinelguard.io/zh-cn/docs/quick-start.html

2.1 原生API

1.引入依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.5</version>
</dependency>

2.定义资源

资源是 Sentinel 的关键概念。它可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。在接下来的文档中,都会用资源来描述代码块。 只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用 方法签名,URL,甚至服务名称作为资源名来标示资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class SentinelExample{
private static final String RESOURCE_NAME = "Test";
public static void main(String[] args) {
//初始化配置规则
initFlowRules();
while(true) {
Entry entry = null;
try {
entry = SphU.entry(RESOURCE_NAME);
//被保护的逻辑
System.out.printLn("hello sentinel");
} catch (BlockException ex){
//处理被流控的逻辑
System.out.printLn("blocked!");
} finally {
if(entry != null){
entry.exit();
}
}
}
}
}

3.定义规则

https://sentinelguard.io/zh-cn/docs/introduction.html

围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。

1
2
3
4
5
6
7
8
9
10
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource(RESOURCE_NAME);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20
rule.setCount(20);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}

4.运行方法,观察控制台

5.理解过程

1
2
3
initFlowRules()
->FlowRuleManager.loadRules(rules)
->private static volatile Map<String, List<FlowRule>> flowRules = new HashMap<>();

可以理解为名称为HelloWorld的资源,目前拥有一个流控规则FlowRule,流控规则中有具体的一些值

1
2
3
4
SphU.entry(RESOURCE_NAME)
->FlowSlot#entry
->checkFlow(resourceWrapper, context, node, count, prioritized)
->Collection<FlowRule> rules = ruleProvider.apply(resource.getName())

也就是在SphU#entry方法中,会根据资源名称拿到对应的流控规则FlowRule

1
2
3
canPassCheck
->passLocalCheck
->canPass

最后在canPass方法中,会判断当前的QPS根据流控规则的配置是否能够通过

6.总结

1
2
a.加载规则到内存 FlowRuleManager#loadRules(rules)
b.定义被保护的资源 SphU#entry

2.2 原生API-Web MVC

(1)定义FlowRuleController及被保护的资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RestController
@RequestMapping("/flow")
public class FlowRuleController {
@RequestMapping("/hello")
public String hello() {
Entry entry = null;
try {
entry = SphU.entry("flow");
System.out.println("web mvc hello sentinel.");
return "web mvc hello sentinel.";
} catch (BlockException ex) {
// 处理被流控的逻辑
System.out.println("web mvc flow limit!");
return "web mvc flow limit!";
} finally {
if (entry != null) {
entry.exit();
}
}
}
}

(2)加载规则到内存

1
2
3
4
5
6
7
8
9
10
@PostConstruct // Spring Bean实例化之后执行
private static void flowRules() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("flow");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(2);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}

(3)启动Spring Boot项目,并调用/flow/hello接口

2.3 @SentinelResource注解

(1)在FlowController中新增一个api接口,并且定义@SentinelResource注解,为value属性指定值, 其实这个属性值就是资源名称

1
2
3
4
5
6
@RequestMapping("/test")
@SentinelResource(value="flow")
public String test() {
System.out.println("@Sentinel hello sentinel.");
return "@Sentinel hello sentinel.";
}

(2)引入依赖

1
2
3
4
5
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.5</version>
</dependency>

(3)定义配置类,实例化SentinelResourceAspect对象

1
2
3
4
5
6
7
@Configuration
public class SentinelConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect(){
return new SentinelResourceAspect();
}
}

(4)重启Spring Boot项目,测试test接口的QPS是否生效

(5)简单分析一下实现原理

其实就是通过AOP给目标代码的前后添加上了SphU#entry相关的逻辑

(6)定义流控异常

1
2
3
4
5
6
7
8
9
10
11
@RequestMapping("/test")
@SentinelResource(value="flow",blockHandler = "handleBlock")
public String test() {
System.out.println("@Sentinel hello sentinel.");
return "@Sentinel hello sentinel.";
}

public String handleBlock(BlockException e){
System.out.println("流控异常: "+e);
return "QPS超过阈值,流控了.";
}

2.4 客户端整合Sentinel控制台

https://sentinelguard.io/zh-cn/docs/dashboard.html

(1)下载控制台

https://github.com/alibaba/Sentinel/releases

(2)启动控制台

1
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.5.jar

(3)访问控制台:http://localhost:8080

(4)客户端引入与sentinel dashboard通信的jar包

1
2
3
4
5
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.5</version>
</dependency>

(5)配置启动参数

1
Dcsp.sentinel.dashboard.server=localhost:8080

3 整合Spring Cloud Alibaba Sentinel

可以注释掉sentinel-core、sentinel-annotation-aspectj、sentinel-transport-simple-http,因为 在sentinel starter中都包含了

(1)引入依赖

1
2
3
4
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

(2)写配置

1
2
3
4
5
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080

(3)重启user服务,访问:http://localhost:8081/user/hello

(4)打开sentinel dashboard,配置流控规则

(5)原理

1
2
3
4
5
6
7
8
a-SphU#entry核心代码在哪 
SentinelWebInterceptor#preHandle
...
Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN)
b-规则什么时候保存到客户端的内存中的?
a-dashboard点击保存按钮
b-微服务收到dashboard推送过来的规则
c-微服务将规则保存到内存中

(6)如何自定义流控之后的异常处理

1
2
3
4
SentinelWebInterceptor#preHandle
->catch(Exception e)
->handleBlockException
->DefaultBlockExceptionHandler#handle

(7)可以替换掉DefaultBlockExceptionHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class JackWebMvcBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
// Return 429 (Too Many Requests) by default.
response.setStatus(429);
PrintWriter out = response.getWriter();
if(e instanceof FlowException){
out.print("Jack flow limit.");
out.flush();
out.close();
}else if(e instanceof DegradeException){
out.print("Jack degrade.");
out.flush();
out.close();
}
}
}

4 Sentinel客户端与服务端通信原理

(1)服务注册+心跳机制:心跳时间间隔默认为10s,可以通过heartbeat-interval-ms进行修改

1
SimpleHttpHeartbeatSender#sendHeartbeat

(2)访问:http://localhost:8720/api,就可以看到sentinel客户端提供了哪些api

1
客户端处理接口:CommandHandler

(3)获取内存中的流控规则:http://localhost:8720/getRules?type=flow

1
2
FetchActiveRuleCommandHandler#handle
-> "flow"

(4)设置内存中的流控规则

1
ModifyRulesCommandHandler#handle 

sentinel入门
http://www.zivjie.cn/2023/03/18/spring框架/springcloud/sentinel/sentinel入门/
作者
Francis
发布于
2023年3月18日
许可协议