• SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化


    Sentinel简介

    随着微服务的流行,服务与服务之间的稳定性变得越来越重要。Sentinel以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

    Sentinel具有如下特性:

    • 丰富的应用场景:承接了阿里巴巴近十年的双十一大促流量的核心场景,例如秒杀,可以实时熔断下游不可用服务。
    • 完备的实时监控:同时提供实时的监控功能。可以在控制台中看到接入应用的单台机器秒级数据,甚至500台以下规模的集群的汇总运行情况。
    • 广泛的开源生态:提供开箱即用的与其他开源框架/库的整合模块,例如与 SpringCloud 、Dubbo、 gRPC的整合。
    • 完善的 SPI 扩展点:提供简单易用、完善的 SPI 扩展点。可以通过实现扩展点,快速的定制逻辑。

    一、安装Sentinel控制台

    Sentinel控制台是一个轻量级的控制台应用,它可用于实时查看单机资源监控及集群资源汇总,并提供了一系列的规则管理功能,如流控规则、降级规则、热点规则等。

    从官网下载Sentinel,这里使用的是sentinel-dashboard-1.7.2.jar文件,下载地址:
    https://github.com/alibaba/Sentinel/releases

    下载完成后再命令行输入以下命令运行Sentinel控制台:

    java -jar sentinel-dashboard-1.7.2.jar
    

    Sentinel控制台默认运行在8080端口上,登录账号和密码均为sentinel,通过如下地址进行访问:http://localhost:8080
    在这里插入图片描述
    Sentinel控制台可以查看单台机器的实时监控数据。
    在这里插入图片描述

    二、创建springboot-sentinel模块

    这里我们创建一个springboot-sentinel模块,用于演示Sentinel的熔断与限流功能。

    在pom.xml中添加相关依赖,这里我们使用Nacos作为注册中心,所以需要同时引入Nacos的依赖。

      <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-parent</artifactId>
          <version>2.2.2.RELEASE</version>
          <relativePath/>
      </parent>
    
      <properties>
          <java.version>1.8</java.version>
          <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
      </properties>
    
      <dependencyManagement>
          <dependencies>
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-dependencies</artifactId>
                  <version>${spring-cloud.version}</version>
                  <type>pom</type>
                  <scope>import</scope>
              </dependency>
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                  <version>2.1.1.RELEASE</version>
                  <type>pom</type>
                  <scope>import</scope>
              </dependency>
          </dependencies>
      </dependencyManagement>
    
      <dependencies>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-web</artifactId>
          </dependency>
    
          <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-sentinel</artifactId>
          </dependency>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
          </dependency>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-starter-openfeign</artifactId>
          </dependency>
    
          <dependency>
              <groupId>com.alibaba.csp</groupId>
              <artifactId>sentinel-datasource-nacos</artifactId>
          </dependency>
    
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-test</artifactId>
              <scope>test</scope>
              <exclusions>
                  <exclusion>
                      <groupId>org.junit.vintage</groupId>
                      <artifactId>junit-vintage-engine</artifactId>
                  </exclusion>
              </exclusions>
          </dependency>
      </dependencies>
    
    
      <build>
          <plugins>
              <plugin>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-maven-plugin</artifactId>
              </plugin>
          </plugins>
      </build>
    

    在application.yml中添加相关配置,主要是配置了Nacos和Sentinel控制台地址:

    server:
      port: 8401
    
    spring:
      application:
        name: springboot-sentinel
      cloud:
        nacos:
          discovery:
            namespace: dev
            server-addr: 192.168.0.130:8848
        sentinel:
          transport:
            # 配置Sentinel dashborad地址
            dashboard: http://localhost:8080
            port: 8719
          datasource:
            - nacos:
                server-addr: 192.168.0.130:8848
                data-id: ${spring.application.name}-flow-rules
                group-id: DEFAULT_GROUP
                data-type: json
                rule-type: flow
    
    service-url:
      user-service: http://192.168.0.130:8501
    
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    feign:
      sentinel:
        enabled: true
    

    三、限流功能

    Sentinel Starter 默认为所有的 HTTP 服务提供了限流埋点,我们也可以通过使用@SentinelResource来自定义一些限流行为。

    四、创建RateLimitController类

    用于测试熔断和限流功能。

    package com.lee.controller;
    
    import com.alibaba.csp.sentinel.annotation.SentinelResource;
    import com.alibaba.csp.sentinel.slots.block.BlockException;
    import com.lee.handle.CustomBlockHandler;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @RestController
    @RequestMapping("/rateLimit")
    public class RateLimitController {
    
        /**
         * 按资源名称限流,需要指定限流处理逻辑
         *
         * @return
         */
        @GetMapping("/byResource")
        @SentinelResource(value = "byResource", blockHandler = "handleException")
        public Map<String,Object> byResource() {
            Map<String,Object> result = new HashMap<>();
            result.put("name","按资源名称限流");
            result.put("code",200);
            return result ;
        }
    
        /**
         * 按url限流,有默认的限流处理逻辑
         *
         * @return
         */
        @GetMapping("byUrl")
        @SentinelResource(value = "byUrl", blockHandler = "handleException")
        public Map<String,Object> byUrl() {
            Map<String,Object> result = new HashMap<>();
            result.put("name","按url限流");
            result.put("code",200);
            return result ;
        }
    
        public Map<String,Object> handleException(BlockException exception) {
            Map<String,Object> result = new HashMap<>();
            result.put("name",exception.getClass().getCanonicalName());
            result.put("code",200);
            return result ;
        }
    
        @GetMapping("/customBlockHandler")
        @SentinelResource(value = "customBlockHandler", blockHandler = "handleException", blockHandlerClass = CustomBlockHandler.class)
        public Map<String,Object> blockHandler() {
            Map<String,Object> result = new HashMap<>();
            result.put("name","限流成功");
            result.put("code",200);
            return result ;
        }
    }
    
    

    1.根据资源名称限流

    我们可以根据@SentinelResource注解中定义的value(资源名称)来进行限流操作,但是需要指定限流处理逻辑。

    流控规则可以在Sentinel控制台进行配置,由于我们使用了Nacos注册中心,我们先启动Nacos和sentinel-service;

    由于Sentinel采用的懒加载规则,需要我们先访问下接口,Sentinel控制台中才会有对应服务信息,我们先访问下该接口:
    http://localhost:8401/rateLimit/byUrl

    在Sentinel控制台配置流控规则,根据@SentinelResource注解的value值:
    在这里插入图片描述
    快速访问上面的接口,可以发现返回了自己定义的限流处理信息:
    在这里插入图片描述

    2.根据URL限流

    我们还可以通过访问的URL来限流,会返回默认的限流处理信息。

    在Sentinel控制台配置流控规则,使用访问的URL:
    在这里插入图片描述

    多次访问该接口,会返回默认的限流处理结果:http://localhost:8401/rateLimit/byUrl

    在这里插入图片描述

    3.自定义限流处理逻辑

    我们可以自定义通用的限流处理逻辑,然后在@SentinelResource中指定。

    创建 CustomBlockHandler 类用于自定义限流处理逻辑:

    package com.lee.handle;
    
    import com.alibaba.csp.sentinel.slots.block.BlockException;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class CustomBlockHandler {
    
        public static Map<String,Object> handleException(BlockException exception) {
    
            Map<String,Object> result = new HashMap<>();
            result.put("name","自定义限流信息");
            result.put("code",200);
            return result ;
        }
    }
    

    在RateLimitController中自定义限流处理逻辑:

    package com.lee.controller;
    
    import com.alibaba.csp.sentinel.annotation.SentinelResource;
    import com.lee.handle.CustomBlockHandler;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @RestController
    @RequestMapping("/rateLimit")
    public class RateLimitController {
    
        @GetMapping("/customBlockHandler")
        @SentinelResource(value = "customBlockHandler", blockHandler = "handleException", blockHandlerClass = CustomBlockHandler.class)
        public Map<String,Object> blockHandler() {
            Map<String,Object> result = new HashMap<>();
            result.put("name","限流成功");
            result.put("code",200);
            return result ;
        }
    }
    

    五、熔断功能

    Sentinel 支持对服务间调用进行保护,对故障应用进行熔断操作,这里我们使用RestTemplate来调用springboot-provider服务所提供的接口来演示下该功能。

    springboot-provider服务提供的接口非常简单:

    package com.lee.controller;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @RestController
    public class TestController {
    
    
        @GetMapping("/user/{id}")
        public Map<String,Object> getInfo(@PathVariable(value = "id") String id) {
    
            if("1".equals(id)) {
                throw new RuntimeException("remote func is fail");
            }
    
            Map<String,Object> result = new HashMap<>();
            result.put("reqData",id);
            result.put("code","200");
    
            return result ;
        }
    }
    

    创建RestTemplate

    package com.lee.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.client.RestTemplate;
    
    @Configuration
    public class CusConfig {
    
        @Bean
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }
    

    添加 CircleBreakerController 类,定义对springboot-provider提供接口的调用:

    package com.lee.controller;
    
    import com.alibaba.csp.sentinel.annotation.SentinelResource;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import java.util.HashMap;
    import java.util.Map;
    
    
    @RestController
    @RequestMapping("/breaker")
    public class CircleBreakerController {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(CircleBreakerController.class);
    
        @Autowired
        private RestTemplate restTemplate;
    
        @Value("${service-url.user-service}")
        private String userServiceUrl;
    
        @GetMapping("/fallback/{id}")
        @SentinelResource(value = "fallback", fallback = "handleFallback")
        public Map<String,Object> fallback(@PathVariable Long id) {
            Map<String,Object> forObject = restTemplate.getForObject(userServiceUrl + "/user/{1}", Map.class, id);
            System.out.println(forObject);
            return forObject;
        }
    
        @GetMapping("/fallbackException/{id}")
        @SentinelResource(value = "fallbackException", fallback = "handleFallback2", exceptionsToIgnore = {NullPointerException.class})
        public Map<String,Object> fallbackException(@PathVariable Long id) {
            if (id == 1) {
                throw new IndexOutOfBoundsException();
            } else if (id == 2) {
                throw new NullPointerException();
            }
    
            return restTemplate.getForObject(userServiceUrl + "/user/{1}", Map.class, id);
        }
    
        public Map<String,Object> handleFallback(Long id) {
            Map<String,Object> result = new HashMap<>();
            result.put("name","service degradation");
            result.put("code",200);
            return result ;
        }
    
        public Map<String,Object> handleFallback2(Long id, Throwable e) {
            LOGGER.error("handleFallback2 id:{},throwable class:{}", id, e.getClass());
            Map<String,Object> result = new HashMap<>();
            result.put("name","service degradation");
            result.put("code",200);
    
            return result ;
        }
    }
    

    启动springboot-provider和springboot-sentinel服务:

    如果id为1 的情况会返回服务降级结果:
    http://localhost:8401/breaker/fallback/1
    在这里插入图片描述
    id为2时正常返回:
    在这里插入图片描述

    由于我们使用了 exceptionsToIgnore 参数忽略了 NullPointerException ,所以我们访问接口报空指针时不会发生服务降级:
    http://localhost:8401/breaker/fallbackException/2

    在这里插入图片描述

    六、与Feign结合使用

    Sentinel也适配了Feign组件,我们使用Feign来进行服务间调用时,也可以使用它来进行熔断。

    首先我们需要在pom.xml中添加Feign相关依赖:

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    

    在application.yml文件中打开Sentinel对Feign的支持:

    # 打开sentinel对feign的支持
    feign:
      sentinel:
        enabled: true
    

    在应用启动类上添加@EnableFeignClients启动Feign的功能;

    创建一个UserService接口,用于定义对springboot-provider服务的调用:

    package com.lee.service;
    
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    
    import java.util.Map;
    
    @FeignClient(value = "springboot-provider", fallback = UserFallbackService.class)
    public interface UserService {
    
        @GetMapping("/user/{id}")
        Map<String,Object> getInfo(@PathVariable Long id);
    }
    

    创建UserFallbackService类实现UserService接口,用于处理服务降级逻辑:

    package com.lee.service;
    
    import org.springframework.stereotype.Component;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @Component
    public class UserFallbackService implements UserService {
    
        @Override
        public Map<String,Object> getInfo(Long id){
    
            Map<String,Object> result = new HashMap<>();
            result.put("name","service lower level");
            result.put("code",200);
    
            return result ;
        }
    }
    
    

    在UserFeignController中使用UserService通过Feign调用springboot-provider服务中的接口:

    package com.lee.controller;
    
    import com.lee.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.Map;
    
    @RestController
    @RequestMapping("/user")
    public class UserFeignController {
    
        @Autowired
        private UserService userService;
    
        @GetMapping("/{id}")
        public Map<String,Object> getUser(@PathVariable Long id) {
            return userService.getInfo(id);
        }
    
    }
    
    

    在启动类SentinelServiceApplication添加@EnableFeignClients注解

    package com.lee;
    
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.openfeign.EnableFeignClients;
    
    @EnableDiscoveryClient
    @SpringBootApplication
    @EnableFeignClients
    public class App {
    
        public static void main(String[] args) {
            SpringApplication.run(App.class, args);
        }
    
    }
    
    

    调用如下接口会发生服务降级,返回服务降级处理信息:http://localhost:8401/user/1
    在这里插入图片描述

    七、使用Nacos存储规则

    默认情况下,当我们在Sentinel控制台中配置规则时,控制台推送规则方式是通过API将规则推送至客户端并直接更新到内存中。一旦我们重启应用,规则将消失。下面我们介绍下如何将配置规则进行持久化,以存储到Nacos为例。

    1. 原理示意图

    在这里插入图片描述
    首先我们直接在配置中心创建规则,配置中心将规则推送到客户端;

    Sentinel控制台也从配置中心去获取配置信息。

    2. 功能演示

    先在pom.xml中添加相关依赖:

    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    

    修改application.yml配置文件,添加Nacos数据源配置:

    spring:
      application:
        name: springboot-sentinel
      cloud:
        nacos:
          discovery:
            namespace: dev
            server-addr: 192.168.0.130:8848
        sentinel:
          transport:
            # 配置Sentinel dashborad地址
            dashboard: http://localhost:8080
            port: 8719
          datasource:
            - nacos:
                server-addr: 192.168.0.130:8848
                data-id: ${spring.application.name}-flow-rules
                group-id: DEFAULT_GROUP
                data-type: json
                rule-type: flow
    

    在Nacos中添加配置:

    在这里插入图片描述
    添加配置信息如下:

    [
        {
            "app":"springboot-sentinel",
            "clusterConfig":{
                "fallbackToLocalWhenFail":true,
                "sampleCount":10,
                "strategy":0,
                "thresholdType":0,
                "windowIntervalMs":1000
            },
            "clusterMode":false,
            "controlBehavior":0,
            "count":1,
            "gmtCreate":1617343597193,
            "gmtModified":1617343597193,
            "grade":1,
            "id":1,
            "ip":"192.168.0.130",
            "limitApp":"default",
            "port":8720,
            "resource":"byUrl",
            "strategy":0
        }
    ]
    

    常规参数解释:

    • resource:资源名称;
    • limitApp:来源应用;
    • grade:阈值类型,0表示线程数,1表示QPS;
    • count:单机阈值;
    • strategy:流控模式,0表示直接,1表示关联,2表示链路;
    • controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
    • clusterMode:是否集群。

    发现Sentinel控制台已经有了如下限流规则:
    在这里插入图片描述

    快速访问测试接口,可以发现返回了限流处理信息:
    在这里插入图片描述

    到这已基本完成对sentinel的基本使用。现在从nacos修改的配置信息能够同步到sentinel,但是从sentinel控制台修改的数据却无法同步到nacos中,这样两边都修改数据信息的话,对导致非常严重的后果。

    3. Sentinel Dashboard集成Nacos实现规则同步

    Sentinel Dashboard的流控规则下的所有操作,都会调用Sentinel-Dashboard源码中的FlowControllerV1类,这个类中包含流控规则本地化 的CRUD操作。

    另外,在com.alibaba.csp.sentinel.dashboard.controller.v2包下存在一个FlowControllerV2类,这个类同样提供流控规则的CRUD,和V1版本不同的是,它可以实现指定数据源的规则拉取和发布。

    @RestController
    @RequestMapping(value = "/v2/flow")
    public class FlowControllerV2 {
    
        private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);
    
        @Autowired
        private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;
    
        @Autowired
        @Qualifier("flowRuleDefaultProvider")
        private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
        @Autowired
        @Qualifier("flowRuleDefaultPublisher")
        private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
    

    FlowControllerV2依赖以下两个非常重要的类:

    • DynamicRuleProvider: 动态规则的拉取,从指定数据源中获取流控规则后在Sentinel Dashboard中展示。
    • DynamicRulePublisher: 动态规则的发布,将在Sentinel Dashboard中修改的规则同步到指定数据源中。

    我们可以扩展这两个类,然后集成Nacos来实现Sentinel Dashboard规则的同步。

    4. Sentinel Dashboard源码修改

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

    修改源码,具体步骤如下:

    1. 使用IDEA工具打开sentinel-dashboard工程。
    2. 在pom.xml中把 sentinel-datasource-nacos 依赖的注释掉。在这里插入图片描述
    3. 修改resources/app/scripts/directives/sidebar/sidebar.html文件中下面这段代码,将dashboard.flowV1改成dashboard.flow,也就是去掉V1。修改之后,会调用FlowControllerV2中的接口。如果不修改,则调用FlowControllerV1中的接口(也可自行修改接口实现)
      在这里插入图片描述
    4. 在com.alibaba.csp.sentinel.dashboard.rule包中创建一个nacos包,并创建一个类用来加载外部化配置。
    
    package com.alibaba.csp.sentinel.dashboard.rule.nacos;
    
    
    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 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() {}
    }
    
    1. 创建一个NacosConfig 配置类
    
    package com.alibaba.csp.sentinel.dashboard.rule.nacos;
    
    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.NacosFactory;
    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.Value;
    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 {
    
        @Value("${sentinel.nacos.serverAddr}")
        private String nacosAddr ;
    
        @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() throws Exception {
    
            Properties properties = new Properties();
            properties.put(PropertyKeyConst.SERVER_ADDR,nacosAddr);
            return  NacosFactory.createConfigService(properties);
        }
    }
    
    
    1. 实现动态从Nacos配置中心获取流控规则
    @Component("flowRuleNacosProvider")
    public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {
    
        @Autowired
        private Converter<String, List<FlowRuleEntity>> converter;
    
        @Value("${sentinel.nacos.group-id}")
        private String groupId ;
    
        @Autowired
        private ConfigService configService ;
    
        @Override
        public List<FlowRuleEntity> getRules(String appName) throws Exception {
    
    //        Properties properties = new Properties();
    //        properties.put(PropertyKeyConst.SERVER_ADDR, nacosAddr);
    //        ConfigService configService = NacosFactory.createConfigService(properties);
    //        String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX, groupId, 5000);
    
    
    //        String serverAddr = "192.168.0.130:8848";
    //        String dataId = "springboot-sentinel-flow-rules";
    //        String group = "DEFAULT_GROUP";
    //        Properties properties = new Properties();
    //        properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
    //        ConfigService configService = NacosFactory.createConfigService(properties);
    //        String rules = configService.getConfig(dataId, group, 5000);
    
            System.out.println(appName+NacosConfigUtil.FLOW_DATA_ID_POSTFIX);
            String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
                    groupId, 5000);
            if (StringUtil.isEmpty(rules)) {
                return new ArrayList<>();
            }
            return converter.convert(rules);
        }
    }
    
    1. 创建一个流控规则发布类,在Sentinel Dashboard上修改完配置后,需要调用该发布方法将数据持久化到Nacos中。
    @Component("flowRuleNacosPublisher")
    public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {
    
        @Autowired
        private Converter<List<FlowRuleEntity>, String> converter;
    
        @Value("${sentinel.nacos.group-id}")
        private String groupId ;
    
        @Autowired
        private ConfigService configService ;
    
        @Override
        public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
            AssertUtil.notEmpty(app, "app name cannot be empty");
            if (rules == null) {
                return;
            }
    
    //        String serverAddr = "192.168.0.241:8848";
    //        String dataId = "springboot-docker-flow-rules";
    //        String group = "DEFAULT_GROUP";
    //        Properties properties = new Properties();
    //        properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
    //        ConfigService configService = NacosFactory.createConfigService(properties);
    //
    //        configService.publishConfig(dataId,group,converter.convert(rules));
    
            configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
                    groupId, converter.convert(rules));
        }
    }
    
    1. 修改FlowControllerV2类,将上面配置的两个类注入进来,表示规则的拉取和规则的发布统一用我们前面定义的两个实例,然后将FlowControllerV2这个类中的代码覆盖FlowControllerV1的代码。
    @RestController
    @RequestMapping(value = "/v2/flow")
    public class FlowControllerV2 {
    
        private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);
    
        @Autowired
        private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;
    
        @Autowired
        @Qualifier("flowRuleNacosProvider")
        private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
        @Autowired
        @Qualifier("flowRuleNacosPublisher")
        private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
    
    1. 在application.properties文件中添加Nacos服务相关配置:
    sentinel.nacos.serverAddr=192.168.0.241:8848
    sentinel.nacos.namespace=
    sentinel.nacos.group-id=DEFAULT_GROUP
    
    1. 打包
    mvn clean package 
    
    1. 修改sentinel控制台数据,则会同步到nacos中

    修改前
    在这里插入图片描述
    修改后
    在这里插入图片描述
    nacos中
    在这里插入图片描述

    项目地址(包含编译好的sentinel1.7.2版本的dashboard):https://gitee.com/enthusiasts/sentinel-nacos.git

    参考文章:
    https://blog.csdn.net/ThinkWon/article/details/103770879
    https://www.jianshu.com/p/deb7daa715af

    充满鲜花的世界到底在哪里
  • 相关阅读:
    XCTF Guess-the-Number
    XCTF 3rd-GCTF-2017 hackme
    XCTF IgniteMe
    hdu 1713 相遇周期
    hdu 2092 整数解(一元二次方程解)
    failed to find romfile "efi-virtio.rom"
    在KVM主机和虚拟机之间共享目录
    Ubuntu下载连接(阿里云镜像)
    ubuntu apt-mirror 同步源到本地
    ubuntu 构建 deb 安装包
  • 原文地址:https://www.cnblogs.com/aliases/p/14693280.html
Copyright © 2020-2023  润新知