SpringCloud Alibaba 改造Sentinel Dashboard將流控規則持久化到Nacos


Sentinel Dashboard集成Nacos目錄:

SpringCloud Alibaba 改造Sentinel Dashboard將流控規則持久化到Nacos  本文

SpringCloud Alibaba 改造Sentinel Dashboard將熔斷規則持久化到Nacos

 

Sentinel是阿里開源的項目,提供了流量控制、熔斷降級、系統負載保護等多個維度來保障服務之間的穩定性。經歷過Alibaba歷屆雙十一的考驗,其性能的卓越性肯定是不言而喻的。

Sentinel Dashboard是Sentinel提供的圖形化控制台,可以通過Sentinel Dashboard維護流控規則、熔斷規則、熱點規則等。

然而,開源版本的Sentinel Dashboard是無法直接應用於生產環境中的,這是因為通過開源版本的Sentinel Dashboard維護的各項規則是存儲於內存中的,當Sentinel Dashboard重啟,則內存中的各項規則也一並丟失,這在生產上是不被允許的。

在Alibaba Sentinel的githup上,有一篇帖子在生產環境中使用Sentinel,介紹了Sentinel的三種規則管理和推送規則,

如下,而開源版本的Sentinel Dashboard使用的就是其中的原始模式,可以看到是不被推薦在生產環境中應用的。

 

而另外兩種方式,pull模式和push模式,前者是使用諸如Nacos,利用Nacos Config的特性將Nacos作為純粹的數據源來使用。當需要對流控規則做修改時,需要到Nacos上進行修改,然后Sentinel Dashboard拉取(Pull)Nacos Config上存儲的規則並展示在Dashboard上。

此種方式下,在Dashboard上維護的規則無法直接存儲到Nacos上,修改規則需要到Nacos上進行,但這樣的話就無法利用Dashboard提供的圖形化界面,在Nacos上維護規則也容易出錯,且對人員的要求高,需要非常熟悉規則配置的參數。同時拉取模式無法保證時效性。

 

所以Sentinel官方推薦的方式是使用Push模式,通過Dashboard配置的規則直接可以存儲在如Nacos之類的數據源上,客戶端通過訂閱Nacos上的配置來拉取規則配置,具體架構如官網上的下圖:

 

這種方式好是好,但是開源的Sentinel Dashboard並未提供實現,來將配置好的規則存儲於第三方的數據源中,需要直接下載Sentinel Dashboard的源代碼,依據選擇的第三方數據源修改代碼予以實現。本文下面的內容就是介紹如何實現Sentinel Dashboard將規則推送至Nacos上,以及各個應用如何訂閱Nacos上的配置實現流控的

 

SpringCloud、SpringCloud Alibaba和SpringBoot三者之間有比較嚴格的版本依賴,在Sentinel開源版本的使用中可能會遇到各種各樣的問題,這其中絕大多數是由於三者版本不匹配導致的,因此建議使用官方推薦的版本。Sentinel官網上並未詳細介紹三者版本之間的關系,而是需要到githup上才能得知具體的依賴關系,介紹的網址如下:

SpringCloud Alibaba版本說明

本文使用的是如下的版本:

 


 

下載Sentinel Dashboard源代碼

下載地址如下:

https://github.com/alibaba/Sentinel/,使用IDE打開sentinel-dashboard項目

 

一. Sentinel Dashboard集成Nacos實現流控規則持久化

 Sentinel Dashboard的流控規則下的所有操作,都會調用com.alibaba.csp.sentinel.dashboard.controller.FlowControllerV1這個類,這個類中包含流控規則本地化(內存中)的CRUD操作,因此流控規則是存儲在內存中的,所以每當重啟Dashboard后,內存中的內容就會丟失。

而com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2中同樣實現了流控規則的CRUD,和V1版本不同的是,它可以實現指定數據源的規則拉取(從指定的數據源中查詢已經配置好的流控規則)和發布(將通過Dashboard維護的內容存儲到指定的數據源中)。

FlowControllerV2中注入了兩個非常重要的類:com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider和com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher分別實現了拉取和發布動作。

這里就需要擴展這兩個類,實現集成Nacos來實現Sentinel Dashboard規則的同步。

1. 首先修改Sentinel Dashboard源碼中的Pom文件,把sentinel-datasource-nacos依賴的<scope>注釋掉

<!-- for Nacos rule publisher sample -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <!-- 使用Nacos作為數據源,需要將scope=test注釋掉 -->
    <!--<scope>test</scope>-->
</dependency>

 

 2. 修改html文件,使得前台維護動作可以調用V2接口

打開:/sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html,按照截圖中的介紹修改此html代碼。

 

 

打開:/sentinel-dashboard/src/main/webapp/resources/app/views/flow_v2.html,注釋掉<回到單機頁面>代碼。注釋掉此段代碼的原因是,當我們修改了sidebar.html中的代碼注釋掉了V1版本的頁面,放開了V2版本的頁面后,V2版本的頁面依然保留了原有的在內存中維護規則的入口,就是這個<回到單機頁面>按鈕。

通過點擊<回到單機頁面>按鈕進入到的維護頁面維護的規則依然是存儲在內存中的,為了避免使用者的誤解,故注釋掉此段代碼。

 

接下來修改簇族鏈路中的流控規則添加按鈕:

 

 打開:\src\main\webapp\resources\app\scripts\controllers\identity.js

修改下圖紅框位置的代碼,將FlowServiceV1修改為FlowServiceV2。

修改前代碼如下:

 

修改后代碼如下:

 

 3:創建DynamicRuleProvider和DynamicRulePublisher新的實現類

3.1 創建靜態類,定義所需常量

/**
 * Nacos常量類
 * @author gang.wang
 * 2021年11月8日
 */
public class NacosConstants {
    
    public static final String DATA_ID_POSTFIX = "-sentinel-flow";
    
    public static final String GROUP_ID = "DEFAULT_GROUP";

}

 

3.2 創建NacosPropertiesConfiguration類,加載application.properties中的Nacos配置

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 加載Nacos配置
 * @author gang.wang
 * 2021年10月31日
 */
@ConfigurationProperties(prefix="sentinel.nacos")
public class NacosPropertiesConfiguration {
    
    /**
     * Nacos服務地址
     */
    private String serverAddr;
    
    private String dataId;
    
    private String groupId = "DEFAULT_GROUP";
    
    private String namespace;

    public String getServerAddr() {
        return serverAddr;
    }

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

    public String getDataId() {
        return dataId;
    }

    public void setDataId(String dataId) {
        this.dataId = dataId;
    }

    public String getGroupId() {
        return groupId;
    }

    public void setGroupId(String groupId) {
        this.groupId = groupId;
    }

    public String getNamespace() {
        return namespace;
    }

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

 

3.3 創建NacosConfiguration類,初始化Nacos配置

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

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
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 com.alibaba.nacos.api.exception.NacosException;

/**
 * Nacos配置類
 * @author gang.wang
 * 2021年10月31日
 */
@EnableConfigurationProperties(NacosPropertiesConfiguration.class)
@Configuration
public class NacosConfiguration {

    @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 ConfigService nacosConfigService(NacosPropertiesConfiguration nacosPropertiesConfiguration) throws NacosException {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, nacosPropertiesConfiguration.getServerAddr());
        properties.put(PropertyKeyConst.NAMESPACE, nacosPropertiesConfiguration.getNamespace());
        return ConfigFactory.createConfigService(properties);
    }
}

 

3.4 創建DynamicRuleProvider的實現類

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.nacos.api.config.ConfigService;

/**
 * 實現從Nacos配置中心獲取流控規則
 * @author gang.wang
 * 2021年11月8日
 */
@Service
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {
    
    private static Logger logger = LoggerFactory.getLogger(FlowRuleNacosProvider.class);
    
    @Autowired
    private NacosPropertiesConfiguration nacosConfigProperties;
    
    @Autowired
    private ConfigService configService;
    
    @Autowired
    private Converter<String, List<FlowRuleEntity>> converter;
    
    @Override
    public List<FlowRuleEntity> getRules(String appName) throws Exception {
        
        //定義dataId 應用名+固定后綴
        String dataId = new StringBuilder(appName).append(NacosConstants.DATA_ID_POSTFIX).toString();
        
        String rules = configService.getConfig(dataId, nacosConfigProperties.getGroupId(), 3000);
        
        logger.info("Pull FlowRule from Nacos Config : {}", rules);
        
        if(StringUtils.isEmpty(rules)) {
            return new ArrayList<>();
        }
        return converter.convert(rules);
    }
}

 

 3.5 創建DynamicRulePublisher的實現類

import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.nacos.api.config.ConfigService;

/**
 * 將通過Sentinel Dashboard上維護的流控規則數據持久化到Nacos中
 * @author gang.wang
 * 2021年11月8日
 */
@Service
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {
    
    private static Logger logger = LoggerFactory.getLogger(FlowRuleNacosPublisher.class);
    
    @Autowired
    private NacosPropertiesConfiguration nacosConfigProperties;
    
    @Autowired
    private ConfigService configService;
    
    @Autowired
    private Converter<List<FlowRuleEntity>, String> converter;

    @Override
    public void publish(String appName, List<FlowRuleEntity> rules) throws Exception {
        
        if(StringUtils.isBlank(appName)) {
            logger.error("傳入的AppName為Null");
            return ;
        }
        
        if(null == rules) {
            logger.error("傳入的流控規則數據為null");
            return ;
        }
        
        String dataId = new StringBuilder(appName).append(NacosConstants.DATA_ID_POSTFIX).toString();
        
        configService.publishConfig(dataId, nacosConfigProperties.getGroupId(), converter.convert(rules));
        
    }
}

4:修改FlowControllerV2中DynamicRuleProvider和DynamicRulePublisher的依賴注入

修改com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2中DynamicRuleProvider和DynamicRulePublisher的依賴注入,引用最新的Nacos Provider和Publisher

修改后如下:

@Autowired
//@Qualifier("flowRuleDefaultProvider") //注釋掉原注入
@Qualifier("flowRuleNacosProvider")//Nacos數據源
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
    
/**
* 動態規則的發布,將在Sentinel Dashboard中修改的規則同步到指定數據源中。
*/
@Autowired
//@Qualifier("flowRuleDefaultPublisher")
@Qualifier("flowRuleNacosPublisher")//Nacos數據源
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;

 

5:添加Nacos配置信息

修改application.properties文件的內容添加Nacos服務器配置。

# 定義Nacos服務器信息
sentinel.nacos.serverAddr=127.0.0.1:8848
sentinel.nacos.namespace=37c7c263-bdf1-41db-9f34-bf1094111111
sentinel.nacos.group-id=DEFAULT_GROUP

 

 6:重新打包並運行我們修改好的Sentinel Dashboard

 啟動Sentinel Dashboard項目,瀏覽器中訪問:http://127.0.0.1:8080/#/login  輸入用戶名/密碼=sentinel/sentinel,就可以順利登陸Sentinel Dashboard控制台了!

 

此時進入控制台后,左邊菜單中還看不到任何菜單信息。因為此刻還沒有任何項目連接到Sentinel Dashboard上。

7:創建SpringBoot應用並連接到Sentinel Dashboard上

7.1 應用的Pom文件中添加sentinel和nacos相關依賴

<?xml version="1.0"?>
<project
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.alibaba.sentinel</groupId>
    <artifactId>sentinel-dashboard-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sentinel-dashboard-test</name>
    <url>http://maven.apache.org</url>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>

        <!-- Spring Cloud -->

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <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>

        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
            <version>3.0.3</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- Database -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        
        <dependency>
             <groupId>com.oracle</groupId>
             <artifactId>ojdbc7</artifactId>
             <version>12.1.0.1</version>
         </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>

        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
        </dependency>

        <!-- Other -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>

    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
View Code

 

7.2 修改應用的yml文件,添加Nacos和Sentinel Dashboard配置

spring:
  application:
    name: sentinel-dashboard-test
  jackson:
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss
  cloud:
    nacos:
      discovery:
        namespace: 37c7c263-bdf1-41db-9f34-bf10948be752
        server-addr: 127.0.0.1:8848
        weight: 1
      config:
        namespace: 37c7c263-bdf1-41db-9f34-bf10948be752
        file-extension: yml
        max-retry: 5
        name: sentinel-dashboard-test
        refresh-enabled: true
        prefix: 
    sentinel:
      transport: 
        dashboard: 127.0.0.1:8080
      datasource: 
        flow: 
          nacos: 
            server-addr: 127.0.0.1:8848
            namespace: 37c7c263-bdf1-41db-9f34-bf10948be752
            data-id: ${spring.application.name}-sentinel-flow
            group-id: DEFAULT_GROUP
            data-type: json
            rule-type: flow

 7.3 創建一個接口

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;

/**
 * @author gang.wang
 * 2021年9月15日
 */
@RestController
public class SentinelDemoOneController {
    
    private Logger logger = LoggerFactory.getLogger(SentinelDemoOneController.class);
    
    @SentinelResource(value = "hello", blockHandler = "blockHandlerHello")
    @GetMapping("/say")
    public String hello() {
        return "hello, Gary!";
    }
public String blockHandlerHello(BlockException ex) {
        logger.error("當前請求已被限流", ex);
        return "當前請求已被限流";
    }

}

7.4 啟動應用並訪問定義的接口

啟動應用,並使用如Postman訪問定義好的/say接口。因為Sentinel Dashboard是使用懶加載的模式檢測需要攔截的接口(http://127.0.0.1:8083/say),因此啟動應用后如果不調用幾次接口,則在Sentinel Dashboard中還是看不到我們定義好的接口信息。

刷新Sentinel Dashboard頁面,可以看到應用了

 點擊實時監控菜單,可以看到剛才我們訪問的接口的監控數據

 

 

點擊流控規則菜單,新建一條流控規則:

這里我們為了驗證方便,將qps的閾值設置為1,即每秒允許一個請求通過。

 

 8:驗證流控規則是否生效

再次使用Postman快速訪問這個接口,可以看到有時候會成功,有時候會失敗,失敗時的返回結果如下:

 

 

9:驗證通過Sentinel Dashboard配置好的流控規則是否正確存儲在Nacos上

訪問Nacos控制台,查看對應的Namespace下是否有dataId = sentinel-dashboard-test-sentinel-flow的配置。

 

 

可見,通過Sentinel Dashboard配置的流控規則已經自動存儲在Nacos中了!

 

 

 

至此,Sentinel Dashboard與Nacos之間對於流控規則的同步已經完成了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM