0%

sentinel集成nacos

sentinel集成nacos

准备

  1. 在nacos上新增相关空间。此处省略。
  2. 先通过 Github 拉取 Sentinel 源码,GitHub 地址:https://github.com/alibaba/Sentinel
  3. 导入到IDEA中
  4. 进入 sentinel-dashboard 模块,所有的修改都在该模块下。

后端代码调整

  1. 修改pom.xml

    大约在104行

    1
    2
    3
    4
    5
    6
    <!-- for Nacos rule publisher sample -->
    <dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <!-- <scope>test</scope>-->
    </dependency>

    找到以上依赖,将scope删除或者注释掉

2.修改配置文件

找到配置文件 application.properties 文件,在末尾添加一下配置:

1
2
3
4
5
6
7
8
9
10
# 服务端口、控制台地址、名称
server.port=9100
csp.sentinel.dashboard.server=127.0.0.1:9100
project.name=sentinel-dashboard
# Nacos地址、账号、密码
sentinel.nacos.serverAddr=10.99.186.131:8848
sentinel.nacos.username=nacos
sentinel.nacos.password=nacos
# 默认命名空间就是 public,不用填写,填了public反而找不到,所以这个置空就行。
sentinel.nacos.namespace=f1c5e720-4d31-4d48-b268-f871e32bdb03

注意:如果需要将规则持久化到nacos的自定义命名空间下,namespace修改成对应的空间名id即可

  1. 调整包结构:复制一份test中的nacos到rule中

    在com.alibaba.csp.sentinel.dashboard.rule包下创建nacos包,并在该包下根据以下结构新建包

    1
    2
    3
    4
    5
    6
    7
    8
    ├─auth #存放授权规则相关类
    ├─degrade #存放降级规则相关类
    ├─flow #存放限流规则相关类
    ├─gateway #存放网关限流规则相关类
    │ ├─api
    │ └─flow
    ├─param #存放热点规则相关类
    └─system #存放系统规则相关类

    image-20230817162740222

    目录介绍:
    FlowRuleNacosProvider: 动态获取Nacos配置中心流控规则
    FlowRuleNacosPublisher: publish上传流控规则到Nacos配置中心
    NacosConfig: Nacos配置
    NacosConfigUtils: 流控规则配置

  2. 增加SentinelNacosProperties文件

    在com.alibaba.csp.sentinel.dashboard.rule.nacos包下创建SentinelNacosProperties.java文件,代码如下:

    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
    package com.alibaba.csp.sentinel.dashboard.rule.nacos;

    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;

    @Component
    @ConfigurationProperties(prefix = "sentinel.nacos")
    public class SentinelNacosProperties {

    private String serverAddr;

    private String namespace;

    private String username;

    private String password;

    private String groupId = "SENTINEL_GROUP";

    public String getServerAddr() {
    return serverAddr;
    }

    public void setServerAddr(String serverAddr) {
    this.serverAddr = serverAddr;
    }

    public String getNamespace() {
    return namespace;
    }

    public void setNamespace(String namespace) {
    this.namespace = namespace;
    }

    public String getUsername() {
    return username;
    }

    public void setUsername(String username) {
    this.username = username;
    }

    public String getPassword() {
    return password;
    }

    public void setPassword(String password) {
    this.password = password;
    }

    public String getGroupId() {
    return groupId;
    }

    public void setGroupId(String groupId) {
    this.groupId = groupId;
    }
    }
  3. 修改NacosConfig文件

    image-20230817163137596

    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
    /*
    * Copyright 1999-2018 Alibaba Group Holding Ltd.
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    * http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    */
    package com.alibaba.csp.sentinel.dashboard.rule.nacos;

    import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.ApiDefinitionEntity;
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.*;
    import com.alibaba.csp.sentinel.datasource.Converter;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.nacos.api.PropertyKeyConst;
    import com.alibaba.nacos.api.config.ConfigFactory;
    import com.alibaba.nacos.api.config.ConfigService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.AutoConfigureAfter;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;

    import java.util.List;
    import java.util.Properties;

    /**
    * @author Eric Zhao
    * @since 1.4.0
    */
    @Configuration
    public class NacosConfig {
    // 注入刚新建的nacos配置文件
    @Autowired
    private SentinelNacosProperties sentinelNacosProperties;
    //====================流控规则 Converter====================
    @Bean
    public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
    return JSON::toJSONString;
    }

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

    //====================降级规则 Converter====================
    @Bean
    public Converter<List<DegradeRuleEntity>, String> degradeRuleEntityEncoder() {
    return JSON::toJSONString;
    }

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

    //====================热点规则 Converter====================
    @Bean
    public Converter<List<ParamFlowRuleEntity>, String> paramsRuleEntityEncoder() {
    return JSON::toJSONString;
    }

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

    //====================系统规则 Converter====================
    @Bean
    public Converter<List<SystemRuleEntity>, String> systemRuleEntityEncoder() {
    return JSON::toJSONString;
    }

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

    //====================授权规则 Converter====================
    @Bean
    public Converter<List<AuthorityRuleEntity>, String> authRuleEntityEncoder() {
    return JSON::toJSONString;
    }


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


    //====================网关API限流规则 Converter====================
    @Bean
    public Converter<List<ApiDefinitionEntity>, String> apiDefinitionEntityEncoder() {
    return JSON::toJSONString;
    }

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

    //====================网关限流规则 Converter====================
    @Bean
    public Converter<List<GatewayFlowRuleEntity>, String> gatewayFlowRuleEntityEncoder() {
    return JSON::toJSONString;
    }

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

    @Bean
    public ConfigService nacosConfigService(SentinelNacosProperties sentinelNacosProperties) throws Exception {
    Properties properties = new Properties();
    // nacos 地址
    properties.put(PropertyKeyConst.SERVER_ADDR, sentinelNacosProperties.getServerAddr());
    // 命令空间
    properties.put(PropertyKeyConst.NAMESPACE, sentinelNacosProperties.getNamespace());
    // nacos 账号
    properties.put(PropertyKeyConst.USERNAME, sentinelNacosProperties.getUsername());
    // nacos 账号密码
    properties.put(PropertyKeyConst.PASSWORD, sentinelNacosProperties.getPassword());
    return ConfigFactory.createConfigService(properties);
    }


    }
  4. 修改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
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    package com.alibaba.csp.sentinel.dashboard.rule.nacos;

    /**
    * @author Eric Zhao
    * @since 1.4.0
    */
    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 PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
    public static final String DEGRADE_DATA_ID_POSTFIX = "-degrade-rules";
    public static final String SYS_DATA_ID_POSTFIX = "-system-rules";
    public static final String AUTH_DATA_ID_POSTFIX = "-auth-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";

    public static final String GATEWAY_FLOW_DATA_ID_POSTFIX = "-gateway-flow-rules";
    public static final String GATEWAY_API_DATA_ID_POSTFIX = "-gateway-api-rules";


    private NacosConfigUtil() {}
    }
  5. 在auth包中创建如下类:

    AuthorityRuleNacosProvider 用于从nacos拉取配置

    AuthorityRuleNacosPublisher 用于控制台向nacos推送配置

    参照上面拷贝的FlowRuleNacosProvider和FlowRuleNacosPublisher文件中的代码创建以上两个文件,完整的代码如下:

    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
    @Component("authRuleNacosProvider")
    public class AuthorityRuleNacosProvider implements DynamicRuleProvider<List<AuthorityRuleEntity>> {

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthorityRuleNacosProvider.class);

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

    @Override
    public List<AuthorityRuleEntity> getRules(String appName) throws Exception {
    String rules = configService.getConfig(appName + NacosConfigUtil.AUTH_DATA_ID_POSTFIX,
    NacosConfigUtil.GROUP_ID, 3000);
    LOGGER.info("get auth rule from nacos, rules : {}", rules);
    if (StringUtil.isEmpty(rules)) {
    return new ArrayList<>();
    }
    return converter.convert(rules);
    }
    }

    @Component("authRuleNacosPublisher")
    public class AuthorityRuleNacosPublisher implements DynamicRulePublisher<List<AuthorityRuleEntity>> {

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthorityRuleNacosPublisher.class);

    @Autowired
    private ConfigService configService;

    @Autowired
    private Converter<List<AuthorityRuleEntity>, String> converter;

    @Override
    public void publish(String app, List<AuthorityRuleEntity> rules) throws Exception {
    AssertUtil.notEmpty(app, "app name cannot be empty");
    if (rules == null) {
    return;
    }
    String convertedRule = converter.convert(rules);
    LOGGER.info("sentinel dashboard publisher auth rules : {}", convertedRule);
    configService.publishConfig(app + NacosConfigUtil.AUTH_DATA_ID_POSTFIX,
    NacosConfigUtil.GROUP_ID, convertedRule, ConfigType.JSON.getType());
    }
    }
  6. 依次创建降级规则、热点规则、系统规则等文件。

    代码大同小异,不同点就是Converter<List<AuthorityRuleEntity>, String> converter中的entity不同,具体的可以回看NacosConfig中的配置,另一个就是data-id不同。这里就不一一罗列代码了。

    修改后文件为:

    image-20230818135724639

  7. controller层开启 Nacos 适配

    修改 com.alibaba.csp.sentinel.dashboard.controller.v2包下的FlowControllerV2,内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //    @Autowired
    // @Qualifier("flowRuleDefaultProvider")
    // private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
    // @Autowired
    // @Qualifier("flowRuleDefaultPublisher")
    // private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;

    @Autowired
    @Qualifier("flowRuleNacosProvider")
    private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
    @Autowired
    @Qualifier("flowRuleNacosPublisher")
    private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;

    注释掉原来的ruleProvider和rulePublisher,引入我们自己创建的。

    AuthorityRuleController:

    修改 com.alibaba.csp.sentinel.dashboard.controlle包下的AuthorityRuleController,内容如下:

    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
    // 增加以下代码
    @Autowired
    @Qualifier("authRuleNacosProvider")
    private DynamicRuleProvider<List<AuthorityRuleEntity>> ruleProvider;

    @Autowired
    @Qualifier("authRuleNacosPublisher")
    private DynamicRulePublisher<List<AuthorityRuleEntity>> rulePublisher;

    @GetMapping("/rules")
    @AuthAction(PrivilegeType.READ_RULE)
    public Result<List<AuthorityRuleEntity>> apiQueryAllRulesForMachine(@RequestParam String app,
    @RequestParam String ip,
    @RequestParam Integer port) {
    if (StringUtil.isEmpty(app)) {
    return Result.ofFail(-1, "app cannot be null or empty");
    }
    if (StringUtil.isEmpty(ip)) {
    return Result.ofFail(-1, "ip cannot be null or empty");
    }
    if (port == null || port <= 0) {
    return Result.ofFail(-1, "Invalid parameter: port");
    }
    if (!appManagement.isValidMachineOfApp(app, ip)) {
    return Result.ofFail(-1, "given ip does not belong to given app");
    }
    try {
    // 注释掉下面一行代码
    // List<AuthorityRuleEntity> rules = sentinelApiClient.fetchAuthorityRulesOfMachine(app, ip, port);
    // 增加以下代码
    List<AuthorityRuleEntity> rules = ruleProvider.getRules(app);
    // 尤其注意增加以下if逻辑,不然运行时会报错,这里踩的坑
    if (rules != null && !rules.isEmpty()) {
    for (AuthorityRuleEntity entity : rules) {
    entity.setApp(app);
    }
    }
    rules = repository.saveAll(rules);
    return Result.ofSuccess(rules);
    } catch (Throwable throwable) {
    logger.error("Error when querying authority rules", throwable);
    return Result.ofFail(-1, throwable.getMessage());
    }
    }

    private boolean publishRules(String app, String ip, Integer port) {
    List<AuthorityRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
    // 注释掉这一行
    // return sentinelApiClient.setAuthorityRuleOfMachine(app, ip, port, rules);
    // 增加以下代码
    try {
    rulePublisher.publish(app, rules);
    return true;
    } catch (Exception e) {
    return false;
    }
    }

    需要增加以及改动的代码,已经在代码上加了注释,请仔细阅读。

    同理修改其他几个controller

    注意:ParamFlowRule

    1
    2
    3
    4
    5
    6
    7
    8
    apiAddParamFlowRule中修改:
    publishRules(entity.getApp(), entity.getIp(), entity.getPort());


    private void publishRules(String app, String ip, Integer port) throws Exception {
    List<ParamFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
    rulePublisher.publish(app, rules);
    }

前端代码调整

  1. sidebar.html

找到webapp/resources/app/scripts/directives/sidebar/sidebar.html文件,

(直接搜dashboard.flowV1定位57行去掉V1):

1
2
3
4
5
6
7
8
<!--<li ui-sref-active="active" ng-if="!entry.isGateway">
<a ui-sref="dashboard.flowV1({app: entry.app})">
<i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则</a>
</li>-->
<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>
  1. identity.js

找到webapp/resources/app/scripts/controllers/identity.js文件,

修改第4行:

1
2
// 'FlowServiceV1'   
'FlowServiceV2',

(直接搜/dashboard/flow/定位第101行):

1
2
// let url = '/dashboard/flow/' + $scope.app;
let url = '/dashboard/v2/flow/' + $scope.app;
  1. flow_v2.html 找到webapp/resources/app/views/flow_v2.html文件,找到以下代码:
1
2
3
<a class="btn btn-default-inverse" style="float: right; margin-right: 10px;" ui-sref="dashboard.flowV1({app: app})">
回到单机页面
</a>

将其注释掉或删掉。

最后这个回到单击页面 会恢复到 默认内存模式