Sentinel介绍

Sentinel是什么

Sentinel是分布式系统的流量防卫兵,以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。详见:Sentinel Github文档Sentinel 介绍Spring Cloud Alibaba Sentinel
整体说来,Sentinel分为两个部分:客户端和服务端,在使用过程中客户端和服务端的版本要保持一致。
针对不同形式的应用和通信方式集成Sentinel客户端的方式略有所不同,参考:快速开始Spring Cloud Alibaba Sentinel开源框架适配多语言支持
服务端提供了一个管理控制台,可以在控制台界面中看到相应的监控信息,以及执行相关的服务治理操作。

Sentinel控制台

源码启动

首先,下载Sentinel最新稳定分支的源代码,加载到IDE(如:IntelliJ IDEA)中,找到控制台的启动类:com.alibaba.csp.sentinel.dashboard.DashboardApplication,添加启动JVM参数:

1
2
3
-Dserver.port=8080 
-Dcsp.sentinel.dashboard server=localhost:8080
-Dproject.name=sentinel-dashboard

运行com.alibaba.csp.sentinel.dashboard.DashboardApplication即可,运行端口为8080。

-Dserver.port=8080是Spring Boot的参数,用于指定Sentinel控制台启动端口为8080,其他的是Sentinel客户端参数
-Dcsp.sentinel.dashboard.server=localhost:8080用于向Sentinel接入端指定控制台的地址
-Dproject.name=sentinel-dashboard用于向Sentinel指定应用名称

经过上述配置,控制台启动后会自动向自己发送心跳。

从Sentinel 1.6.0开始,Sentinel控制台支持简单的登录功能,默认用户名和密码都是sentinel

  • -Dsentinel.dashboard.auth.username=sentinel用于指定控制台的登录用户名为sentinel
  • -Dsentinel.dashboard.auth.password=123456用于指定控制台的登录密码为123456,如果省略这两个参数,默认用户和密码均为sentinel
  • -Dserver.servlet.session.timeout=7200用于指定服务端会话的过期时间,如7200表示7200秒;60m表示60分钟,默认为30分钟

全部的配置项可以参考启动配置项文档

值得注意的是,以最新稳定分支“1.8”为例,源码的编译级别默认为Java 17,如下所示:

如果未安装JDK 17环境,在运行控制台启动类时可能会报错:java: 警告: 源发行版 17 需要目标发行版 17

解决办法:修改Sentinel/pom.xml文件中maven-compiler-plugin插件的<release>参数为指定的JDK版本(如:8)即可。

单机模式

可以从release 页面下载最新版本的控制台jar包。
使用如下命令启动控制台:

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

其中-Dserver.port=8080用于指定Sentinel控制台端口为8080
从Sentinel 1.6.0起,Sentinel控制台引入基本的登录功能,默认用户名和密码都是sentinel。可以参考鉴权模块文档配置用户名和密码。

启动Sentinel控制台后,在浏览器中输入控制台访问地址:http://localhost:8080,输入用户名和密码即可进入Sentinel控制台界面。

集群模式

Sentinel开源版本模式只支持单机模式部署,集群环境的部署需要进行改造,主要涉及的点有如下几个方面:

  • 规则管理及推送:集成配置中心,如:ZooKeeper,Nacos
  • 监控:集成数据库,支持历史查询
  • 权限控制:细粒度的权限控制

集成Nacos实现规则管理和推送

  • 规则管理

关于规则管理部分,需要在Sentinel控制台进行改造的内容分为两个部分:前端页面和保存配置的后端接口,由于Nacos配置中心中有3个概念:命名空间,DataId和Group,因此需要同步体现在UI界面中,在添加或修改配置的时候可以修改其对应值;在保存的时候将参数信息提交给后端接口。后端接口在接收到管理后台前端的提交的参数后,将其同步保存到Nacos配置中心即可。针对后端接口的改造涉及两个方面,其一:增加对应Nacos配置中心的3个参数(命名空间,DataId和Group),其二:集成Nacos客户端,将数据保存到Nacos配置中心后端数据库中。
如下代码示例展示了如何使用Nacos客户端接口将数据保存到Nacos服务端:

1
2
3
4
5
6
7
String dataId = ""; // Nacos配置项DataId
String group = ""; // Nacos配置项组
String content = ""; // 这是一个JSON数组,保存相应的规则JSON字符串
Properties properties = new Properties();
properties.setProperty("serverAddr", serverAddr);
ConfigService configService = NacosFactory.createConfigService(this.properties);
boolean result = configService.publishConfig(dataId, group, content)
  • 规则推送

规则推送是针对应用而言的,当Sentinel控制台集成Nacos配置中心后,当新建或修改规则配置信息后,Sentinel控制台将数据推送给Nacos配置中心。在利用Nacos的推送机制使得应用端能及时接收到新建或修改后的配置信息。
关于集成Nacos配置中心实现规则推送的实现比较简单,只需要在应用中集成sentinel-datasource-nacos组件即可,如下示例:

1
2
3
4
5
6
<!-- 集成Nacos配置中心,从Nacos配置中心获取规则配置数据 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.5</version>
</dependency>

在应用端订阅相应的规则配置项即可:

1
2
3
4
5
6
7
8
// 从Nacos配置中心订阅限流规则配置项
String remoteAddress = "localhost:8848";
String flowRuleGroupId = "Sentinel_Demo_FlowRule";
String flowRuleDataId = "com.alibaba.csp.sentinel.demo.flow.rule";
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(remoteAddress, groupId, dataId,
source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
}));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());

监控数据持久化

在开源版Sentinel控制台中,默认是将数据保存到到内存的(InMemoryMetricsRepository),并且只保存最近5分钟的数据,这对于历史数据的查询非常不友好。如果要实现监控数据的持久化存储,只需要实现接口MetricsRepository即可,同时将Bean注入到Spring容器中。另外,还需要在MetricFetcherMetricController中注入这个持久化Bean。

权限控制

在开源版Sentinel控制台中,对权限的控制非常粗糙,相当于只有一个具备最高权限的管理员用户;在做权限校验时也非常简单,只检查超级管理员是否已经登录,如果未登录则跳转到登录页面;如果已经登录则可以执行所有操作。
登录入口在AuthController中,做登录检查在DefaultAuthorizationInterceptor拦截器中实现。另外,对于需要登录后才能执行的操作均使用注解AuthAction进行标注。

改造参考:
在生产环境中使用 Sentinel
Sentinel 控制台(集群流控管理)
动态规则扩展
日志

商业版Sentinel:
MSE Sentinel 控制台
AHAS Sentinel 控制台

客户端接入Sentinel控制台

Sentinel可以简单的分为Sentinel核心库和控制台,核心库不依赖控制台,但是结合控制台可以取得最好的效果。所谓客户端接入控制台就是建立Sentinel核心库与Sentinel控制台之间的联系。
那么,这里有一个疑问:是否可以只使用Sentinel的核心库而不用接入控制台呢?答案是肯定的,当然可以只使用Sentinel核心库也能使用其相应的服务治理功能。但是,缺少服务治理规则可视化展示和编辑功能是不完善的,且易用性将会大打折扣。

不同类型的客户端应用接入Sentinel控制台的方式以及配置参数有所区别,客户端接入的详细步骤参考Wiki 文档

Sentinel服务治理规则的配置可以分为两种方式:API,集成配置中心(如下Nacos),如下将以限流控制为例分别说明API方式和集成Nacos配置中心方式进行治理规则的配置与应用。

API方式配置治理规则

API方式配置服务治理规则是一种硬编码方式,如果需要添加新的规则就必须重新编码,这种方式不适合在生产环境使用,仅能作为测试验证。

1
2
3
4
5
6
7
8
9
10
// 如下硬编码限流规则需要在应用启动成功之后确保被加载
List<FlowRule> flowRules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("flowRuleQps"); // 设置资源名称
rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // 设置限流模式,根据QPS限流
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT); // 设置限流动作,直接决绝
rule.setCount(1); // 设置最大qps阈值
flowRules.add(rule);

FlowRuleManager.loadRules(flowRules);

在使用服务治理规则时有2种方式:注解,SphU.entry()方法。其中注解方式可能需要依赖特定的环境,比如:Spring AOP。在使用Spring Boot框架时通常需要添加如下依赖:

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

<!-- 支持注解方式定义资源 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.5</version>
</dependency>

然后使用注解SentinelResource定义需要进行服务治理的资源信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 通过注解将方法定义为资源
@SentinelResource(value = "flowRuleQps", entryType = EntryType.IN, fallback = "flowRuleQpsFallback")
public Object flowRuleQps(String name) {
return String.format("Hello: %s, now: %s, thread: %s",
name, System.currentTimeMillis(), Thread.currentThread().getName());
}

// flowRuleQps方法限速降级时调用该方法
public Object flowRuleQpsFallback(String name, Throwable t) {
System.out.println(String.format("exception class: %s, msg: %s", t.getClass(), t.getMessage()));
return String.format("Halooooo: %s, now: %s, thread: %s",
name, System.currentTimeMillis(), Thread.currentThread().getName());
}

通常来讲,使用注解方式定义资源信息会更加简洁;但是当无法使用注解方式时,可以直接使用Sentinel提供的SphU.entry()方法定义服务治理资源信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
Entry entry = null;
try {
entry = SphU.entry("flowRuleQps");
// 被保护的逻辑写在这里
System.out.println(String.format("qpsFlowRule: %s", System.currentTimeMillis()));
} catch (BlockException ex) {
// 处理被流控的逻辑
// 比如:给哪些被限流的请求返回一个降级结果
} finally {
if (entry != null) {
entry.exit();
}
}

值得注意的是:在直接使用SphU.entry()方法时,不同的限流规则可能存在一些语法上的区别,需要区别处理。

集成Nacos配置中心配置和推送治理规则

如果将服务治理规则数据放在配置中心,那么就不用通过API方式硬编码服务治理规则信息,这将大大带来灵活性。

1
2
3
4
5
6
<!-- 集成Nacos配置中心,从Nacos配置中心获取规则配置数据 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.5</version>
</dependency>

在应用中通过如下方式订阅配置中心的治理规则更新:

1
2
3
4
5
6
7
8
// 如下示例展示了定义在配置中心的限流规则
String remoteAddress = "localhost:8848";
String flowRuleGroupId = "Sentinel_Demo_FlowRule";
String flowRuleDataId = "com.alibaba.csp.sentinel.demo.flow.rule";
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(remoteAddress, flowRuleGroupId, flowRuleDataId,
source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
}));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());

在Nacos配置中心添加如下限流规则信息:

  • DataId: com.alibaba.csp.sentinel.demo.flow.rule
  • Group: Sentinel_Demo_FlowRule
  • 配置内容:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    [
    {
    "resource": "FlowRule_qps",
    "controlBehavior": 0,
    "count": 3.0,
    "grade": 1,
    "strategy": 0
    }
    ]

在Spring Cloud Alibaba项目中使用Sentinel

如下以在Spring Cloud应用中接入Sentinel控制台为例进行说明。

第一步:添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 使用sentinel进行服务治理 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>

<!-- 集成Nacos配置中心,从Nacos配置中心获取规则配置数据 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.5</version>
</dependency>

<!-- 从配置中心解析JSON格式的治理规则 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.48</version>
</dependency>

第二步:添加配置参数

1
2
3
4
# Sentinel客户端本地启动HTTP API Server的端口号,对应启动配置项参数:csp.sentinel.api.port
spring.cloud.sentinel.transport.port=8179
# Sentinel控制台地址
spring.cloud.sentinel.transport.dashboard=localhost:8080

第三步:从配置中心订阅治理规则信息

1
2
3
4
5
6
7
8
// 限流规则
String remoteAddress = "localhost:8848";
String flowRuleGroupId = "SpringCloudAlibaba_Sentinel_Demo_FlowRule";
String flowRuleDataId = "spring.cloud.alibaba.sentinel.demo.flow.rule";
ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(remoteAddress, flowRuleGroupId, flowRuleDataId,
source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
}));
FlowRuleManager.register2Property(flowRuleDataSource.getProperty());

第四步:通过注解定义资源

1
2
3
4
5
6
7
8
9
10
11
12
13
@GetMapping("/doOrder")
@SentinelResource(value = "doOrder", entryType = EntryType.IN, fallback = "doOrderFallback")
public String doOrder(@RequestHeader("X-Token") String token) {
// 1.模拟远程调用获取商品信息
String info = "";

// 2.模拟返回成功信息
return info;
}

public String doOrderFallback(@RequestHeader("X-Token") String token) {
return "下单失败,熔断处理。";
}

在Spring Cloud Alibaba项目中集成Sentinel除了配置参数稍有区别之外,其他使用方式均是一致的。
此外,在Spring Cloud alibaba项目中使用Sentinel实现服务治理后,就无需使用OpenFeign的熔断配置了(fallback参数配置)。