SpringCloud-Sentinel

1. Sentinel简介

官方地址:https://github.com/alibaba/Sentinel/

中文文档:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

Sentinel 分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。

2. Sentinel控制台(1.8.1)

下载地址:https://github.com/alibaba/Sentinel/releases

中文文档:https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0

下载后的文件:sentinel-dashboard-1.8.1.jar

启动命令:nohup java -Dserver.port=9090 -Dcsp.sentinel.dashboard.server=localhost:9090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar &

注:端口号根据具体情况修改,jar包的名称根据版本号修改

浏览器访问控制台:http://localhost:9090 默认账号密码 :sentinel/sentinel

3. 客户端

以order-service为例

3.1 添加依赖

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

3.2 添加配置文件

1
2
3
4
5
6
7
8
spring:
cloud:
sentinel:
transport:
#控制台地址
dashboard: 127.0.0.1:9090
#客户端监控 API 的端口(默认是 8719)若被占用,则自动+1
#port: 9999

3.3 测试

4. 流控配置

4.1 基于QPS

根据请求地址配置qps流控规则,当 QPS 超过某个阈值的时候,则采取措施进行流量控制

测试流控规则

查看编辑流控规则,在左侧边栏找到流控规则进行操作

4.2 基于并发线程数

用于保护业务线程池不被慢调用耗尽,如果超出阈值,新的请求会被立即拒绝,类似于信号量隔离。

  • 编写测试代码
1
2
3
4
5
6
7
8
9
10
@RequestMapping("/sayHi")
public String sayHi(String name) {
System.out.println("Hi " + name);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Hi " + name;
}
  • 重启order-service服务

  • 打开sentinel控制台,这时候发现之前添加的流控规则没有了,是因为流控规则会下发到微服务,微服务如果重启,则流控规则会消失,可以持久化配置解决,后面会讲到

  • 浏览器请求一次http://127.0.0.1:8080/order-service/api/v1/order/demo/sayHi?name=aacopy

  • 簇点链路中找到**/api/v1/order/demo/sayHi**,点击流控

  • 再次访问sayHi链接,3秒内访问超过2次,则会看到限流提示Blocked by Sentinel (flow limiting)

5. 熔断降级

​ 对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一,熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置

​ 文档:https://github.com/alibaba/Sentinel/wiki/熔断降级

5.1 熔断策略

5.1.1 慢调用比例

TODO

5.1.2 异常比例

  • 编写测试代码
1
2
3
4
5
6
7
8
Random random = new Random();
@RequestMapping("/exceptionProportion")
public String exceptionProportion() {
if (random.nextInt() % 2 == 0) {
throw new RuntimeException();
}
return "success";
}

  • 快速刷新访问地址,触发降级,提示”*Blocked by Sentinel (flow limiting)*“

6. 自定义异常

实现BlockExceptionHandler并且重写handle方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Component
public class OrderBlockExceptionHandler implements BlockExceptionHandler {

public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
Map<String,Object> backMap=new HashMap<>();
if (e instanceof FlowException){
backMap.put("code",-1);
backMap.put("msg","限流-异常啦");
}else if (e instanceof DegradeException){
backMap.put("code",-2);
backMap.put("msg","降级-异常啦");
}else if (e instanceof ParamFlowException){
backMap.put("code",-3);
backMap.put("msg","热点-异常啦");
}else if (e instanceof SystemBlockException){
backMap.put("code",-4);
backMap.put("msg","系统规则-异常啦");
}else if (e instanceof AuthorityException){
backMap.put("code",-5);
backMap.put("msg","认证-异常啦");
}

httpServletResponse.setStatus(200);
httpServletResponse.setHeader("content-Type","application/json;charset=UTF-8");
httpServletResponse.getWriter().write(JSON.toJSONString(backMap));
}
}

添加限流,降级等操作,触发流控测试

7. feign和sentinel整合

  • 添加配置项
1
2
3
4
#开启Feign对Sentinel的支持
feign:
sentinel:
enabled: true
  • 创建容错类

创建fallback文件夹编写实现类GoodsServiceFallback

1
2
3
4
5
6
7
8
@Service
public class GoodsServiceFallback implements GoodsService {
@Override
public String getGoodsById(String id) {
System.out.println("Fallback服务:" + id);
return "ID为"+ id + "的默认商品";
}
}
  • 配置feign容错类

在feignClient接口上的注解@FeignClient增加fallback参数

1
@FeignClient(value = "goods-service", fallback = GoodsServiceFallback.class)

8. Nacos规则配置持久化

8.1 需求

  • Sentinel默认在控制台(Dashboard)中添加流控降级规则后,将配置规则信息推送给Sentinel客户端,配置的规则记录在内存中,如果客户端重启,规则也就丢失。

  • Sentinel的流控规则持久化到nacos中,并且在Sentinel控制台中修改流控规则,需要同步修改nacos的值,并且服务立即生效

8.2 官方相关文档:

8.3 方案:DataSource 扩展

官方推荐:通过控制台设置规则后将规则推送到统一的规则中心(Nacos、Zookeepe),客户端实现 ReadableDataSource 接口端监听规则中心实时获取变更

客户端部分:(实现上图 2,3步骤)

  • 最简单的方式是继承 AbstractDataSource 抽象类,该抽象类已经实现了ReadableDataSource接口,在其构造方法中添加监听器,并实现 readSource() 从指定数据源读取字符串格式的配置数据。

  • 目前官方已经实现了基于Nacos的数据源,NacosDataSource,这个类继承了抽象类AbstractDataSource

  • 客户端只需要引入sentinel-datasource-nacos依赖

  • 通过Nacos配置参数创建NacosDataSource数据源对象,并且将该数据源注册到指定的规则管理器中,如下将Nacos数据源注册到流控规则管理器中

    1
    2
    ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(remoteAddress, groupId, dataId, parser);
    FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
  • 该操作可以放在spring容器启动完成后,执行注册操作。

控制台部分:(实现上图1步骤)

  • Sentinel 控制台提供 DynamicRulePublisher(推送规则) 和 DynamicRuleProvider (拉取规则)接口用于实现应用维度的规则推送和拉取

  • 官方提供了流程规则的示例代码,参照示例代码,对降级,热点,系统,权限做相应的改造

  • 官方提供了 Nacos、ZooKeeper 和 Apollo 的推送和拉取规则实现示例(位于 test 目录下)

  • 以 Nacos 为例,只需在 FlowControllerV2 中指定对应Nacos的 bean 即可开启 Nacos 适配。前端页面需要手动切换,或者修改前端路由配置(sidebar.html 流控规则路由从 dashboard.flowV1 改成 dashboard.flow 即可,簇点链路页面对话框需要自行改造

    1
    2
    3
    4
    5
    6
    @Autowired
    @Qualifier("flowRuleNacosProvider")
    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
    @Autowired
    @Qualifier("flowRuleNacosPublisher")
    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
  • 默认 Nacos 适配的 dataId 和 groupId 约定如下:

    • groupId: SENTINEL_GROUP
    • 流控规则 dataId: {appName}-flow-rules,比如应用名为 appA,则 dataId 为 appA-flow-rules
    • 这些约定配置NacosConfigUtil类中定义
    • 扩展的降级,热点,系统,权限等这些配置就需要在这里定义
  • 官方还提供了String和流控规则对象转换类,在NacosConfig类中,在做扩展时也需要将其他规则转换器注册到spring容器中,在controller操作时需要用到

8.4 控制台Dashboard改造

8.4.1 下载控制台源码

根据项目具体版本获取对应的源码,本笔记使用1.7.1版本

github地址:https://github.com/alibaba/Sentinel

克隆:https://github.com/alibaba/Sentinel.git

切换到tag1.7.1的代码

8.4.2 修改控制台代码sentinel-dashboard

  1. 修改pom.xml

    • 找到<artifactId>sentinel-datasource-nacos</artifactId>

    • <scope>test</scope>删掉

  2. 在test目录下

    • 将com.alibaba.csp.sentinel.dashboard.rule.nacos文件夹复制到main对应的目录下

      image-20220316022119911
  3. 修改com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfig文件

    nacos的地址从配置文件获取,同时将降级,热点,系统,权限转换器注入到spring容器中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    @Configuration
    public class NacosConfig {

    @Value("${nacos.server-addr:localhost}")
    private String serverAddr;

    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
    return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
    return s -> JSON.parseArray(s, FlowRuleEntity.class);
    }

    @Bean
    public Converter<List<AuthorityRuleEntity>, String> authorityRuleEntityEncoder() {
    return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<AuthorityRuleEntity>> authorityRuleEntityDecoder() {
    return s -> JSON.parseArray(s, AuthorityRuleEntity.class);
    }

    @Bean
    public Converter<List<DegradeRuleEntity>, String> degradeRuleEntityEncoder() {
    return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<DegradeRuleEntity>> degradeRuleEntityDecoder() {
    return s -> JSON.parseArray(s, DegradeRuleEntity.class);
    }

    @Bean
    public Converter<List<ParamFlowRuleEntity>, String> paramFlowRuleEntityEncoder() {
    return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<ParamFlowRuleEntity>> paramFlowRuleEntityDecoder() {
    return s -> JSON.parseArray(s, ParamFlowRuleEntity.class);
    }

    @Bean
    public Converter<List<SystemRuleEntity>, String> systemRuleEntityEncoder() {
    return JSON::toJSONString;
    }

    @Bean
    public Converter<String, List<SystemRuleEntity>> systemRuleEntityDecoder() {
    return s -> JSON.parseArray(s, SystemRuleEntity.class);
    }

    @Bean
    public ConfigService nacosConfigService() throws Exception {
    return ConfigFactory.createConfigService(serverAddr);
    }
    }
  4. 约定nacos配置项定义,修改NacosConfigUtil

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public final class NacosConfigUtil {

    public static final String GROUP_ID = "SENTINEL_GROUP";

    public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";
    public static final String DEGRADE_DATA_ID_POSTFIX = "-degrade-rules";
    public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
    public static final String SYSTEM_DATA_ID_POSTFIX = "-system-rules";
    public static final String AUTHORITY_DATA_ID_POSTFIX = "-authority-rules";
    public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map";

    /**
    * cc for `cluster-client`
    */
    public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = "-cc-config";
    /**
    * cs for `cluster-server`
    */
    public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = "-cs-transport-config";
    public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = "-cs-flow-config";
    public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = "-cs-namespace-set";

    private NacosConfigUtil() {}
    }
  5. application.properties配置文件中添加配置

    1
    nacos.server-addr=192.168.80.128:8848

8.4.3 流控规则

流控规则官方文档中已经讲了一部分,参照文档进行改造。

  1. 修改com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2文件

    bean使用nacos

    1
    2
    3
    4
    5
    6
    @Autowired
    @Qualifier("flowRuleNacosProvider")
    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
    @Autowired
    @Qualifier("flowRuleNacosPublisher")
    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
  2. 搜索sidebar.html文件,找关键字flowV1,改为flow

    1
    2
    3
    4
    <li ui-sref-active="active" ng-if="!entry.isGateway">
    <a ui-sref="dashboard.flow({app: entry.app})">
    <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a>
    </li>
  3. 搜索identity.js文件,找关键字FlowServiceV1,改为FlowServiceV2

8.4.4 降级规则

因为官方只有流控规则的改造说明,降级规则需要自己完成,可以参考流控规则FlowControllerV1FlowControllerV2版本的变动,做一下改造。

核心思路

  • 实现DynamicRulePublisher(推送规则) 接口,实现publish方法,用于规则推送配置中心,通过Nacos的configServicepublishConfig方法实现
  • 实现DynamicRuleProvider (拉取规则)接口,实现getRules方法,用于规则拉取,通过Nacos的configServicegetConfig方法实现
  • 在Controller中,获取规则的方法中将原来拉去规则的地方改为dynamicRuleProvider.getRules(app);方式
  • 在保存修改删除规则方法中,同步推送到配置中心dynamicRulePublisher.publish(app, rules);

代码实现

  1. 推送实现类DegradeRuleNacosPublisher

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Component
    public class DegradeRuleNacosPublisher implements DynamicRulePublisher<List<DegradeRuleEntity>> {

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<List<DegradeRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<DegradeRuleEntity> rules) throws Exception {
    AssertUtil.notEmpty(app, "app name cannot be empty");
    if (rules == null) {
    return;
    }
    configService.publishConfig(app + NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX,
    NacosConfigUtil.GROUP_ID, converter.convert(rules));
    }
    }
  2. 拉取实现类DegradeRuleNacosProvider

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Component
    public class DegradeRuleNacosProvider implements DynamicRuleProvider<List<DegradeRuleEntity>> {

    @Autowired
    private ConfigService configService;
    @Autowired
    private Converter<String, List<DegradeRuleEntity>> converter;

    @Override
    public List<DegradeRuleEntity> getRules(String appName) throws Exception {
    String rules = configService.getConfig(appName + NacosConfigUtil.DEGRADE_DATA_ID_POSTFIX,
    NacosConfigUtil.GROUP_ID, 3000);
    if (StringUtil.isEmpty(rules)) {
    return new ArrayList<>();
    }
    return converter.convert(rules);
    }
    }
  3. DegradeController

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    @Controller
    @RequestMapping(value = "/degrade", produces = MediaType.APPLICATION_JSON_VALUE)
    public class DegradeController {

    private final Logger logger = LoggerFactory.getLogger(DegradeController.class);

    @Autowired
    private InMemoryRuleRepositoryAdapter<DegradeRuleEntity> repository;

    @Autowired
    private DynamicRuleProvider<List<DegradeRuleEntity>> ruleProvider;
    @Autowired
    private DynamicRulePublisher<List<DegradeRuleEntity>> rulePublisher;

    @ResponseBody
    @RequestMapping("/rules.json")
    @AuthAction(PrivilegeType.READ_RULE)
    public Result<List<DegradeRuleEntity>> queryMachineRules(String app) {

    if (StringUtil.isEmpty(app)) {
    return Result.ofFail(-1, "app can't be null or empty");
    }
    try {
    List<DegradeRuleEntity> rules = ruleProvider.getRules(app);
    rules = repository.saveAll(rules);
    return Result.ofSuccess(rules);
    } catch (Throwable throwable) {
    logger.error("queryApps error:", throwable);
    return Result.ofThrowable(-1, throwable);
    }
    }

    @ResponseBody
    @RequestMapping("/new.json")
    @AuthAction(PrivilegeType.WRITE_RULE)
    public Result<DegradeRuleEntity> add(String app, String ip, Integer port, String limitApp, String resource,
    Double count, Integer timeWindow, Integer grade) {
    if (StringUtil.isBlank(app)) {
    return Result.ofFail(-1, "app can't be null or empty");
    }
    if (StringUtil.isBlank(ip)) {
    return Result.ofFail(-1, "ip can't be null or empty");
    }
    if (port == null) {
    return Result.ofFail(-1, "port can't be null");
    }
    if (StringUtil.isBlank(limitApp)) {
    return Result.ofFail(-1, "limitApp can't be null or empty");
    }
    if (StringUtil.isBlank(resource)) {
    return Result.ofFail(-1, "resource can't be null or empty");
    }
    if (count == null) {
    return Result.ofFail(-1, "count can't be null");
    }
    if (timeWindow == null) {
    return Result.ofFail(-1, "timeWindow can't be null");
    }
    if (grade == null) {
    return Result.ofFail(-1, "grade can't be null");
    }
    if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {
    return Result.ofFail(-1, "Invalid grade: " + grade);
    }
    DegradeRuleEntity entity = new DegradeRuleEntity();
    entity.setApp(app.trim());
    entity.setIp(ip.trim());
    entity.setPort(port);
    entity.setLimitApp(limitApp.trim());
    entity.setResource(resource.trim());
    entity.setCount(count);
    entity.setTimeWindow(timeWindow);
    entity.setGrade(grade);
    Date date = new Date();
    entity.setGmtCreate(date);
    entity.setGmtModified(date);
    try {
    entity = repository.save(entity);
    publishRules(entity.getApp());
    } catch (Throwable throwable) {
    logger.error("Failed to add degrade rule", throwable);
    return Result.ofThrowable(-1, throwable);
    }
    return Result.ofSuccess(entity);
    }

    @ResponseBody
    @RequestMapping("/save.json")
    @AuthAction(PrivilegeType.WRITE_RULE)
    public Result<DegradeRuleEntity> updateIfNotNull(Long id, String app, String limitApp, String resource,
    Double count, Integer timeWindow, Integer grade) {
    if (id == null) {
    return Result.ofFail(-1, "id can't be null");
    }
    if (grade != null) {
    if (grade < RuleConstant.DEGRADE_GRADE_RT || grade > RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT) {
    return Result.ofFail(-1, "Invalid grade: " + grade);
    }
    }
    DegradeRuleEntity entity = repository.findById(id);
    if (entity == null) {
    return Result.ofFail(-1, "id " + id + " dose not exist");
    }

    if (StringUtil.isNotBlank(app)) {
    entity.setApp(app.trim());
    }

    if (StringUtil.isNotBlank(limitApp)) {
    entity.setLimitApp(limitApp.trim());
    }
    if (StringUtil.isNotBlank(resource)) {
    entity.setResource(resource.trim());
    }
    if (count != null) {
    entity.setCount(count);
    }
    if (timeWindow != null) {
    entity.setTimeWindow(timeWindow);
    }
    if (grade != null) {
    entity.setGrade(grade);
    }
    Date date = new Date();
    entity.setGmtModified(date);
    try {
    entity = repository.save(entity);
    if (entity == null) {
    return Result.ofFail(-1, "save entity fail");
    }
    publishRules(entity.getApp());
    } catch (Throwable throwable) {
    logger.error("Failed to update degrade rule", throwable);
    return Result.ofThrowable(-1, throwable);
    }
    return Result.ofSuccess(entity);
    }

    @ResponseBody
    @RequestMapping("/delete.json")
    @AuthAction(PrivilegeType.DELETE_RULE)
    public Result<Long> delete(Long id) {
    if (id == null) {
    return Result.ofFail(-1, "id can't be null");
    }

    DegradeRuleEntity oldEntity = repository.findById(id);
    if (oldEntity == null) {
    return Result.ofSuccess(null);
    }

    try {
    repository.delete(id);
    publishRules(oldEntity.getApp());
    } catch (Exception e) {
    return Result.ofFail(-1, e.getMessage());
    }
    return Result.ofSuccess(id);
    }

    private void publishRules(/*@NonNull*/ String app) throws Exception {
    List<DegradeRuleEntity> rules = repository.findAllByApp(app);
    rulePublisher.publish(app, rules);
    }
    }

8.4.5 热点,系统,权限规则

改造方式和流控规则一样

8.5 改造客户端

  1. 添加依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>

    <dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
  2. 添加配置信息

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    spring:
    application:
    name: api-gateway
    cloud:
    nacos:
    server-addr: 192.168.80.128:8848
    sentinel:
    transport:
    dashboard: 127.0.0.1:9001
    datasource:
    flow:
    nacos:
    server-addr: ${spring.cloud.nacos.server-addr}
    namespace: public
    group-id: SENTINEL_GROUP
    data-id: ${spring.application.name}-flow-rules
    rule-type: flow
    degrade:
    nacos:
    server-addr: ${spring.cloud.nacos.server-addr}
    namespace: public
    group-id: SENTINEL_GROUP
    data-id: ${spring.application.name}-degrade-rules
    rule-type: degrade
    param:
    nacos:
    server-addr: ${spring.cloud.nacos.server-addr}
    namespace: public
    group-id: SENTINEL_GROUP
    data-id: ${spring.application.name}-param-rules
    rule-type: param-flow
    system:
    nacos:
    server-addr: ${spring.cloud.nacos.server-addr}
    namespace: public
    group-id: SENTINEL_GROUP
    data-id: ${spring.application.name}-system-rules
    rule-type: system
    authority:
    nacos:
    server-addr: ${spring.cloud.nacos.server-addr}
    namespace: public
    group-id: SENTINEL_GROUP
    data-id: ${spring.application.name}-authority-rules
    rule-type: authority
  3. 添加初始化类,用于Sentinel规则持久化DataSource初始化,SentinelNacosDataSourceInit

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    @Component
    @Slf4j
    public class SentinelNacosDataSourceInit implements ApplicationRunner {

    @Autowired
    private SentinelProperties sentinelProperties;

    @Override
    public void run(ApplicationArguments args) {
    //获取配置文件中的规则信息
    Map<String, DataSourcePropertiesConfiguration> datasource = sentinelProperties.getDatasource();
    if(CollectionUtils.isEmpty(datasource)) {
    return;
    }
    datasource.values().forEach(ds -> {
    NacosDataSourceProperties nacosDsp = ds.getNacos();
    if(nacosDsp!=null) {
    //获取规则类型
    RuleType ruleType = nacosDsp.getRuleType();
    switch (ruleType) {
    case FLOW:
    ReadableDataSource<String, List<FlowRule>> flowRuleDataSource = new NacosDataSource<>(nacosDsp.getServerAddr(), nacosDsp.getGroupId(), nacosDsp.getDataId(),
    source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
    }));
    FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
    log.info("Sentinel 流控规则注册完成......");
    break;
    case DEGRADE:
    ReadableDataSource<String, List<DegradeRule>> degradeRuleDataSource = new NacosDataSource<>(nacosDsp.getServerAddr(), nacosDsp.getGroupId(), nacosDsp.getDataId(),
    source -> JSON.parseObject(source, new TypeReference<List<DegradeRule>>() {
    }));
    DegradeRuleManager.register2Property(degradeRuleDataSource.getProperty());
    log.info("Sentinel 降级规则注册完成......");
    break;
    case PARAM_FLOW:
    ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleDataSource = new NacosDataSource<>(nacosDsp.getServerAddr(), nacosDsp.getGroupId(), nacosDsp.getDataId(),
    source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {
    }));
    ParamFlowRuleManager.register2Property(paramFlowRuleDataSource.getProperty());
    log.info("Sentinel 热点规则注册完成......");
    break;
    case SYSTEM:
    ReadableDataSource<String, List<SystemRule>> systemRuleDataSource = new NacosDataSource<>(nacosDsp.getServerAddr(), nacosDsp.getGroupId(), nacosDsp.getDataId(),
    source -> JSON.parseObject(source, new TypeReference<List<SystemRule>>() {
    }));
    SystemRuleManager.register2Property(systemRuleDataSource.getProperty());
    log.info("Sentinel 系统规则注册完成......");
    break;
    case AUTHORITY:
    ReadableDataSource<String, List<AuthorityRule>> authorityRuleDataSource = new NacosDataSource<>(nacosDsp.getServerAddr(), nacosDsp.getGroupId(), nacosDsp.getDataId(),
    source -> JSON.parseObject(source, new TypeReference<List<AuthorityRule>>() {
    }));
    AuthorityRuleManager.register2Property(authorityRuleDataSource.getProperty());
    log.info("Sentinel 授权规则注册完成......");
    break;
    default:
    break;
    }
    }
    });
    }
    }

8.6 验证

  1. 启动nacos
  2. 启动客户端
  3. 启动Sentinel控制台
  4. 请求客户端的接口,在控制台修改流控配置,查看nacos是否同步更新,测试流控是否生效

8.7 代码地址

https://gitee.com/aacopy/sentinel-dashboard.git