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

文章目录

  • Sentinel简介
  • 一、安装Sentinel控制台
  • 二、创建springboot-sentinel模块
  • 三、限流功能
  • 四、创建RateLimitController类
    • 1.根据资源名称限流
    • 2.根据URL限流
    • 3.自定义限流处理逻辑
  • 五、熔断功能
  • 六、与Feign结合使用
  • 七、使用Nacos存储规则
    • 1. 原理示意图
    • 2. 功能演示
    • 3. Sentinel Dashboard集成Nacos实现规则同步
    • 4. Sentinel Dashboard源码修改

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
SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第1张图片
Sentinel控制台可以查看单台机器的实时监控数据。
SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第2张图片

二、创建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值:
SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第3张图片
快速访问上面的接口,可以发现返回了自己定义的限流处理信息:
SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第4张图片

2.根据URL限流

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

在Sentinel控制台配置流控规则,使用访问的URL:
SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第5张图片

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

SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第6张图片

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
SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第7张图片
id为2时正常返回:
SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第8张图片

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

SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第9张图片

六、与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
SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第10张图片

七、使用Nacos存储规则

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

1. 原理示意图

SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第11张图片
首先我们直接在配置中心创建规则,配置中心将规则推送到客户端;

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中添加配置:

SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第12张图片
添加配置信息如下:

[
    {
        "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控制台已经有了如下限流规则:
SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第13张图片

快速访问测试接口,可以发现返回了限流处理信息:
SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第14张图片

到这已基本完成对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 依赖的注释掉。SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第15张图片
  3. 修改resources/app/scripts/directives/sidebar/sidebar.html文件中下面这段代码,将dashboard.flowV1改成dashboard.flow,也就是去掉V1。修改之后,会调用FlowControllerV2中的接口。如果不修改,则调用FlowControllerV1中的接口(也可自行修改接口实现)
    SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第16张图片
  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中

修改前
SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第17张图片
修改后
SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第18张图片
nacos中
SpringCloud-Sentinel实现服务限流、熔断、降级,整合Nacos实现持久化_第19张图片

项目地址(包含编译好的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

你可能感兴趣的:(微服务,spring,cloud,alibaba)