• Spring Cloud Alibaba Sentinel 高可用防护


    1.导言

    服务级联调用:当服务A故障时,服务B调用失败,但还会一直重试。高并发的情况下,会有很多服务B的线程调用服务A,每个线程都会阻塞等待,直到超时,积累到一定程度后,服务B也会崩溃,变为不可用。同样的道理,上游的服务C也会奔溃,变为不可用,这样产生了服务级联调用的雪崩,导致系统整体不可用。所以我们需要一套完善的机制,来保护系统,例如限流,熔断降级,防止服务被压垮,防止发生系统雪崩,阿里的Sentinel就是一套成熟的防护体系,就像一个哨兵一样,时刻保护我们的系统。

    Sentinel 的用法很简单,服务只需要添加 Sentinel 的依赖,就具有限流、降级等防护功能了。但具体如何防护呢?,例如希望此服务的某个接口的 QPS 达到 5 时就限流,这就需要设定一个 Sentinel 规则。有了规则之后,Sentinel 便会根据规则来保护服务。那么规则怎么设置呢?手写肯定不现实,Sentinel 提供了一个管理控制台,界面化设置规则,规则会被自动推送给服务。

    2.服务整合

    服务添加 Sentinel 依赖
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
    <!--sentinel 自己的依赖-->
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>

    开启属性配置

    management:
     endpoints:
       web:
        exposure:
          include: '*'
    访问 Sentinel 端点信息:如果能访问成功,就已经是整合成功了。
    http://localhost:8001/actuator/sentinel

    3.搭建 Sentinel 控制台

    下载编译源码

    https://github.com/alibaba/Sentinel
     
    编译源码
    cd sentinel-dashboard
    mvn clean package
    启动 成功端口是8080,默认帐号密码 sentinel,因为sentinel 是懒加载,所以第一次是空的。
    java -jar sentinel-dashboard-1.7.0.jar
    服务整合控制台
    spring:
     application:
       name: service-provider
    cloud:
      sentinel:
        transport:
          dashboard: localhost:8080

    4.URL限流与资源限流

    URL限流比较好理解,就是根据访问路径进行限流控制。例如有一个接口:/user/get,就可以对其添加限流规则。“资源”是 Sentinel 中的重要概念,可以被调用的都可以视为资源,例如接口、Service。对资源设置一个名称,就可以对其添加限流规则。
     

    流控效果验证

    快速多几次访问 /hello 这个接口。 因为设置的限流是 QPS 阈值为 1,所以很容易触发限流规则。

    流控规则项说明:

    资源名:这里默认使用的接口地址
    针对来源:可以指定来自哪个服务的请求才适用这条规则,默认是default,也就相当于不区分来源
    阈值类型:可以根据 QPS,也就是每秒请求数,进行设置,也可以根据已经使用的线程数来设置
    单机阈值:设置此限流规则的阈值数。例如阈值类型选择了 QPS,阈值设置为1,意思就是当每秒请求数达到 1 以后就限流,阈值类型如果选择了线程数,意思就是处理请求的线程数达到 1 以上就限流。
    流控模式:直接,就是针对这个接口本身
    流控效果:快速失败,直接失败,抛异常

    根据资源进行限流

    对一个测试接口设置为资源,在 Sentinel Console 中对其设置限流规则,验证限流生效。

    @GetMapping("/hello")
    @SentinelResource(value = "res_hello")
    public String hello(@RequestParam String name) {
      return "hello " + name + "!";
    }

    在 Console 中添加测试接口的限流规则

    与之前的对 URL 添加流控规则的界面一致。只是“资源名”这项不再是接口的 URL,变成了我们定义的资源名。验证方式和被限流后的效果都与之前URL方式一致。

    5.关联限流与链路限流

    假设一个 Service 中有2个接口:查询、修改。当查询接口访问量特别大时,必然要影响修改接口,可能会导致修改接口无法处理请求。如果业务上,修改功能优先于查询功能,这就出现了一种限流的需求:“当修改接口的访问量达到一定程度时,就对查询接口限流”。这就是“关联限流”,限流规则不是自己设置的,而是被别人影响的。

    持续大量访问 /hi 接口,使其触发阈值例如通过脚本持续访问 /hi :

    while true; 
    do curl -X GET "http://localhost:8001/hi?name=a" ;
    done;

    访问接口 /hello,应已经被限流,相当是hi接口访问量特别高的时候,hello接口被限流了。

    这个微服务中,2个API都需要调用这个Service资源,需要做限流保护。如果查询接口请求多,导致这个资源被限流,那么修改接口也就用不了了,很冤枉。这就出现了一种限流的需求:“这个Service资源只对查询接口限流,不影响修改接口”。这就是“链路限流”,执行限流时要判断流量是从哪儿来的。

    创建2个测试接口(/testa,/testb),都调用同一个Service(设置为 SentinelResource)。在 Sentinel Console 中对Service设置限流规则,指定入口资源为 /testa。验证 /testa 被限流时,/testb 正常。

    package com.example.demo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class TestController {
        @Autowired
        private CommonService commonService;
    
        @GetMapping("/testa")
        public String testa() {
            return "test a : " + commonService.getName();
        }
        @GetMapping("/testb")
        public String testb() {
            return "test b : " + commonService.getName();
        }
    
    }

    构建2个测试接口,1个Service,指定其为 SentinelResource,2个接口都调用此Service

    package com.example.demo;
    
    import com.alibaba.csp.sentinel.annotation.SentinelResource;
    import org.springframework.stereotype.Service;
    
    @Service
    public class CommonService {
        @SentinelResource("getName")
        public String getName(){
            return "dell";
        }
    }

    Sentinel Console 中对Service设置链路限流,入口资源指定 /testa

    注意点版本有问题。

        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Greenwich.SR5</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>

    6.预热与排队等待

    预热限流

    秒杀开始!海量用户同时请求购买接口。这一瞬间冲击非常大,即使接口设置了限流阈值,一瞬间打满也是很危险的。在秒杀开始之前,这个接口的请求量是比较少的,由空闲状态切换为繁忙状态,我们希望这个过程是逐步的,而不是突然的。这种场景就需要使用“预热 Warm Up”的流控方式。

    假设有个接口的特点是间歇性突发流量,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,属于时忙时闲。正常限流方式会导致大量请求被丢弃。我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝超出阈值的请求。这种场景就需要使用“排队等待”的流控方式。

    预热的处理机制:
    预热流控会设置一个初始阈值,默认是设置阈值的 1/3,在经过预期的时间后,达到设置的阈值。例如设置:阈值=10,预期时间=10秒。则初始阈值为 3,10秒内,阈值逐步增加,10秒后增加到10。使用的是令牌桶算法。
     
    创建一个项目,构建1个测试接口
    package com.example.demo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class TestController {
        @GetMapping("/hi")
        public String hi(){
            return "hi
    ";
        }
    }
    集成 Sentinel 与 Sentinel Console
    Sentinel Console 中对接口设置预热 Warm Up 限流,阈值设为 10 QPS,预热时间设为 10 秒
     
    排队等待

    排队等待的处理机制:
    此方式会严格控制请求通过的间隔时间,让请求以均匀的速度通过,对应的是漏桶算法。例如阈值 QPS = 2,那么就是 500ms 才允许通过下一个请求。

    构建1个测试接口
    Sentinel Console 中对接口设置“排队等待”限流,阈值设为 1 QPS,超时时间设为 1000 毫秒,超时时间是指每个队列最长的等待时间

     持续访问 /hello 接口,会看到匀速通过的效果,例如通过脚本持续访问 /hello :

    while true; 
    do curl -X GET "http://localhost:8001/hello?name=a" ;
    done;

    7.热点限流与系统限流

    /product/query?id=1&name=phone&price=100
    这个API中包括3个参数,假设其中“name”参数是使用最多的,这个接口的绝大部分请求都含有“name”参数,可以称其为“热点参数”。我们可以针对这个热点参数设置接口的限流,当请求带有此参数时,如果达到阈值就触发限流,否则不限流。

    热点规则机制

    @GetMapping("hotparam")
    @SentinelResource(value = "hotparam")
    public String hotparam(String type, String name){ ... }

    参数索引从0开始,按照接口中声明的顺序,此例子中“0”指“type”,“1”指“name”。可以对资源 hotparam 设置热点限流,例如参数 0 的阈值为 1,窗口时长 1秒,一旦在时间窗口内,带有指定索引的参数的 QPS 达到阈值后,会触发限流。还可以对此参数的值进行特殊控制,例如参数值为“5”或者“10”时的阈值,根据值独立控制阈值。

    点击新增热点规则,选择高级选项

     

    注意:
    热点限流只支持“QPS 限流模式”
    针对参数值时,参数类型必须是基本类型(byte int long float double boolean char)或者 String

    系统限流

    之前的限流规则都是针对接口资源的,如果每个资源的阈值都没有达到,但 系统能力不足了怎么办?所以,我们需要针对系统情况来设置一定的规则,系统保护规则是应用整体维度的,而不是资源维度的。
    系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

    系统规则模式
    Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为限流指标,进行自适应系统保护。当
             系统 load1 超过设定的阈值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护。
             系统容量由系统的 maxQps * minRt 估算得出。
             设定参考值: CPU cores * 2.5。
    CPU 使用率:当系统 CPU 使用率超过阈值 即触发系统保护(取值范围 0.0-1.0),比较灵敏。
    平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值 即触发系统保护,单位是毫秒。
    并发线程数:当单台机器上所有入口流量的并发线程数达到阈值 即触发系统保护。
    入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值 即触发系统保护。

    系统规则设置界面

     RT 效果验证

    1. 阈值设置为 10
    2. 持续访问接口 /test-a(其中有睡眠时间),应看到限流的效果。例如通过脚本持续访问 /test-a :

    while true; 
    do curl -X GET "http://localhost:8001/test-a";
    done;

    入口QPS 效果验证:
    1. 阈值设置为 3
    2. 批量持续访问3个测试接口,应看到限流的效果

    并发线程数 效果验证:

    阈值设置为 3

    编写多线程测试代码访问接口,应看到限流效果

    package com.example.demo;
    
    import org.springframework.web.client.RestTemplate;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class TestThread {
        public static void main(String[] args) {
            // 定义一个线程池
            // 创建一个 RestTemplate
            // 循环启动线程发送请求
    
            RestTemplate restTemplate = new RestTemplate();
            ExecutorService executorService = Executors.newFixedThreadPool(10);
            for (int i= 0; i< 50; i++){
                executorService.submit(() -> {
                    try{
                        System.out.println(restTemplate.getForObject("http://localhost:8081/hi", String.class));
                    }catch (Exception e){
                        System.out.println("exception: " + e.getMessage());
                    }
                });
            }
        }
    }

    8.降级规则

    除了流量控制以外,对调用链路中不稳定的资源进行 熔断降级 也是保障高可用的重要措施之一。Sentinel 熔断降级会在调用链路中某个资源不正常时,对这个资源的调用进行限制,让请求快速失败,避免导致级联错误。当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断。

    RT 降级策略

     创建测试接口

    @GetMapping("/degrade-rt")
    public String test_degrade_rt(){
    try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
        return "ok";
    }

    设置降级规则,阈值 1ms,时间窗口 3秒

    持续请求“/degrade-rt”,应显示降级效果

    while true; 
    do curl -X GET "http://localhost:8081/degrade-rt";
    done;

    异常比例降级策略

     

     降级规则设置界面 0.1就是百分之十 ,时间窗口60秒,每秒错误百分之10 就会开始降级策略直到时间窗口结束。

     创建测试接口

    @GetMapping("/degrade-exception-rate")
    public String test_degrade_exception_rate() throws Exception {
    throw new Exception("rate");
    }

    持续请求“/degrade-exception-rate”,应显示降级效果

    while true; 
    do curl -X GET "http://localhost:8081/degrade-exception-rate";
    done;

    异常数降级策略

    降级规则设置界面 在一分钟内异常数大于三次,就会被熔断降级直到时间窗口结束

     

    创建测试接口

    @GetMapping("/degrade-exception-num")
    public String test_degrade_exception_num() throws Exception {
    throw new Exception("rate");
    }

    持续请求“/degrade-exception-num”,应显示降级效果

    while true; 
    do curl -X GET "http://localhost:8001/degrade-exception-num";
    done;

     9.RestTemplate 与 Feign 整合 Sentinel

    服务会调用其他服务,调用方式常用的是 RestTemplate 与 Feign,对于 Sentinel 来讲他们都是可以被保护的资源,所以我们需要学习一下 RestTemplate 与 Feign 如何与 Sentinel 整合,还有如何处理限流与降级后的异常。

    整合流程

    服务消费者 添加依赖

                    <dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-actuator</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>
    

    添加配置

    server:
      port: 8082
    spring:
      application:
        name: service-consumer
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
        sentinel:
          transport:
            dashboard: localhost:8080
    management:
      endpoints:
        web:
          exposure:
            include: '*'
    @Configuration
    public class ConsumerConfig {
        @Bean
        @SentinelRestTemplate
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }

    RestTemplate 限流与降级异常处理

     定义异常处理类

    package com.example.demo;
    
    import com.alibaba.cloud.sentinel.rest.SentinelClientHttpResponse;
    import com.alibaba.csp.sentinel.slots.block.BlockException;
    import org.springframework.http.HttpRequest;
    import org.springframework.http.client.ClientHttpRequestExecution;
    
    public class ExceptionUtil {
        public static SentinelClientHttpResponse handleBlock(
                HttpRequest request,
                byte[] body,
                ClientHttpRequestExecution execution,
                BlockException ex
        ){
            System.out.println("handleblock ex class: " + ex.getClass().getCanonicalName());
            return new SentinelClientHttpResponse("my block info");
        }
    
        public static SentinelClientHttpResponse handleFallback(
                HttpRequest request,
                byte[] body,
                ClientHttpRequestExecution execution,
                BlockException ex
        ){
            System.out.println("handle fallback ex class: " + ex.getClass().getCanonicalName());
            return new SentinelClientHttpResponse("my fallback info");
        }
    }

    在@SentinelRestTemplate 注解中添加异常处理的配置 fallbackClass降级异常用什么类 fallback处理方法。blockHandlerClass限流异常的类, blockHandler处理方法 

    @SentinelRestTemplate(fallbackClass = ExceptionUtil.class, fallback = "handleFallback",
    blockHandlerClass = ExceptionUtil.class, blockHandler = "handleException")

    验证步骤

    Sentinel Console 中对此 RestTemplate 调用添加限流,阈值设为 1

    快速多次访问“/resttemplate_sentinel”,应看到自定义的限流信息

    Sentinel Console 中对此 RestTemplate 调用添加 RT 类型的降级规则,RT 设为 1,时间窗口设为 1

    快速多次访问“/resttemplate_sentinel”,应看到自定义的降级信息

    Feign 整合 Sentinel

    属性配置 Feign Sentinel 开关

    feign:
     sentinel:
       enabled: true

    创建 service-provider 与 service-consumer,整合 nacos、Sentinel、Feign

    Sentinel Console 中针对此 Feign 调用添加限流,阈值设为 1

    快速多刷几次接口 “/hellofeign”,应显示限流效果

    定义异常处理类

    package com.example.demo;
    
    import feign.hystrix.FallbackFactory;
    import org.springframework.stereotype.Component;
    
    @Component
    public class FeignClientServiceFallbackFactory implements FallbackFactory<FeignClientService> {
    
        @Override
        public FeignClientService create(Throwable throwable) {
            return new FeignClientService() {
                @Override
                public String hello(String name) {
                    System.out.println(throwable);
                    return "my exception info";
                }
            };
        }
    }

     @FeignClient 注解中添加异常处理的配置

    package com.example.demo;
    
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @FeignClient(name = "service-provider", fallbackFactory = FeignClientServiceFallbackFactory.class)
    public interface FeignClientService {
        @GetMapping("/hello")
        public String hello(@RequestParam("name") String name);
    }

    10.错误信息自定义与区分来源

    之前我们添加限流规则时,有一项“针对来源”一直都是使用的默认“default”,就是不区分来源。假设一个场景,service A 的API“/query”的主要调用者是 service B,如果不区分来源,肯定会对 Service C 不公平。需要对 Service B 特殊关照,这就可以使用“针对来源”。

     之前我们被限流或者降级后,看到的提示信息都是:

    Blocked by Sentinel (flow limiting)

    效果演示时没问题,但实际环境下这种信息会让我们比较困扰,到底是被限流了,还是被降级了呢?如果能针

    对不同的情况给出不同的信息就好了,这个需求比较好实现。

    如何判别来源

    Sentinel 提供了“RequestOriginParser ”,我们只需要实现其中的“parseOrigin”方法,提供获取来源的方法即可,例如

    package com.example.demo;
    
    import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    
    @Component
    public class MyRequestOriginParser implements RequestOriginParser {
        @Override
        public String parseOrigin(HttpServletRequest httpServletRequest) {
            //根据业务自定义
            return httpServletRequest.getParameter("from");
        }
    }

    Sentinel Console 中为“/hellofeign”设置限流,针对来源设置为“chrome”,阈值设为 1

    访问接口 “/hellofeign?name=a&from=chrome”,应显示限流效果

    授权规则

    “针对来源”还有一个用处:用于实现 授权规则。“流控应用”项与“针对来源”是一样的,“白名单”表示允许访问,“黑名单”表示不允许访问。例如:“流控应用”设为“service-user”,“授权类型”选择“黑名单”,表示“service-user”这个应用不允许访问此资源。

     访问接口 “/hellofeign?name=a&from=“service-user” 成功,其他失败。

    根据错误类型自定义错误信息

    Sentinel 已经定义了不同类型的异常,包括 FlowException、DegradeException、ParamFlowException、SystemBlockException、AuthorityException。我们只需要定义一个异常处理类,针对不同的异常做不同的处理即可,例如:

    package com.example.demo;
    
    import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
    import com.alibaba.csp.sentinel.slots.block.BlockException;
    import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
    import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
    import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
    import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
    import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @Component
    public class MyUrlBlockHandler implements UrlBlockHandler {
        @Override
        public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
            // 1. 根据不同的异常类型,定义不同的错误信息
            String errmsg = "";
            if(e instanceof FlowException){
                errmsg = "限流";
            }else if(e instanceof DegradeException){
                errmsg = "降级";
            }else if(e instanceof ParamFlowException){
                errmsg = "热点参数";
            }else if(e instanceof SystemBlockException){
                errmsg = "系统规则";
            }else if(e instanceof AuthorityException){
                errmsg = "授权规则";
            }
    
            // 2. 输出错误信息
            httpServletResponse.setStatus(500);
            httpServletResponse.setCharacterEncoding("utf-8");
            httpServletResponse.setContentType("application/json;charset=utf-8");
            new ObjectMapper().writeValue(
                    httpServletResponse.getWriter(), errmsg
            );
        }
    }

    11.规则持久化

    之前我们实践 Sentinel 时,每次我们重启应用 Sentinel 控制台中的规则就都没有了,需要重新设置,这是为什么?因为应用的 Sentinel 规则是保存在内存中的。生产环境中,我们需要做好规则持久化。Sentinel 为我们提供了丰富的数据源工具包,便于集成各类数据源,我们需要自己开发代码进行对接。

     

     

     生成环境建议推模式

    改造流程

     从sentinel 源码中找到sentinel-dashboard并引入

    新建service-sentinel-nacos 模块 pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.5.RELEASE</version>
            <relativePath/> 
        </parent>
        <groupId>com.example</groupId>
        <artifactId>service-sentinel-nacos</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
        <name>service-sentinel-nacos</name>
        <description>Demo project for Spring Cloud alibaba</description>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</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>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
        
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Greenwich.SR5</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>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>

    改配置

    server:
      port: 8081
    spring:
      application:
        name: service-sentinel-nacos
      cloud:
        sentinel:
          transport:
            dashboard: localhost:8080
          datasource:
            flow:
              nacos:
                serverAddr: localhost:8848
                dataId: ${spring.application.name}-flow-rules
                groupId: SENTINEL_GROUP
                ruleType: FLOW
                dataType: json
    management:
      endpoints:
        web:
          exposure:
            include: '*'

    nacos 新建配置

    改造sentinel 控制台

     添加依赖

            <!-- for Nacos rule publisher sample -->
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-datasource-nacos</artifactId>
    <!--            <scope>test</scope>-->
            </dependency>

    全pom.xml如下

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <parent>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-parent</artifactId>
            <version>1.7.0</version>
        </parent>
    
        <artifactId>sentinel-dashboard</artifactId>
        <packaging>jar</packaging>
    
        <properties>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <spring.boot.version>2.0.5.RELEASE</spring.boot.version>
            <curator.version>4.0.1</curator.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-core</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-web-servlet</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-transport-simple-http</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-parameter-flow-control</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-api-gateway-adapter-common</artifactId>
                <version>${project.version}</version>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>${spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
                <version>${spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <version>${spring.boot.version}</version>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.14</version>
            </dependency>
    
            <dependency>
                <groupId>commons-lang</groupId>
                <artifactId>commons-lang</artifactId>
                <version>2.6</version>
            </dependency>
    
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
                <version>4.5.3</version>
            </dependency>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpcore</artifactId>
                <version>4.4.5</version>
            </dependency>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpasyncclient</artifactId>
                <version>4.1.3</version>
            </dependency>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpcore-nio</artifactId>
                <version>4.4.6</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
            </dependency>
    
            <!-- for Nacos rule publisher sample -->
            <dependency>
                <groupId>com.alibaba.csp</groupId>
                <artifactId>sentinel-datasource-nacos</artifactId>
    <!--            <scope>test</scope>-->
            </dependency>
            <!-- for Apollo rule publisher sample -->
            <dependency>
                <groupId>com.ctrip.framework.apollo</groupId>
                <artifactId>apollo-openapi</artifactId>
                <version>1.2.0</version>
                <scope>test</scope>
            </dependency>
    
            <!--for Zookeeper rule publisher sample-->
            <dependency>
                <groupId>org.apache.curator</groupId>
                <artifactId>curator-recipes</artifactId>
                <version>${curator.version}</version>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.mockito</groupId>
                <artifactId>mockito-core</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>com.github.stefanbirkner</groupId>
                <artifactId>system-rules</artifactId>
                <version>1.16.1</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <finalName>sentinel-dashboard</finalName>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>${spring.boot.version}</version>
                    <configuration>
                        <fork>true</fork>
                        <mainClass>com.alibaba.csp.sentinel.dashboard.DashboardApplication</mainClass>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
    
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>${maven.compiler.source}</source>
                        <target>${maven.compiler.target}</target>
                    </configuration>
                </plugin>
    
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>${maven.deploy.version}</version>
                    <configuration>
                        <skip>true</skip>
                    </configuration>
                </plugin>
            </plugins>
    
            <resources>
                <resource>
                    <directory>src/main/resources</directory>
                    <filtering>true</filtering>
                </resource>
    
                <resource>
                    <directory>src/main/webapp/</directory>
                    <excludes>
                        <exclude>resources/node_modules/**</exclude>
                    </excludes>
                </resource>
            </resources>
        </build>
    </project>

    将test包下 com.alibaba.csp.sentinel.dashboard.rule 文件夹复制到 main 下。

    修改 FlowControllerV2 

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

    修改前端源码

    webapp => resources => app => scripts => directives =>  sidebar => sidebar.html

    打开注释

           <li ui-sref-active="active" ng-if="entry.appType==0">
                <a ui-sref="dashboard.flow({app: entry.app})">
                  <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则 v1</a>
              </li>

    全html 如下

    <div class="navbar-default sidebar" role="navigation" style="overflow-y: auto;">
      <div class="sidebar-nav navbar-collapse">
        <ul class="nav in" id="side-menu">
          <li class="sidebar-search">
            <div class="input-group" style="">
              <input type="text" class="form-control highlight-border" placeholder="应用名" ng-model="searchApp">
              <span class="input-group-btn">
                <button class="btn btn-secondary btn-default-inverse" type="button">搜索</button>
              </span>
            </div>
          </li>
          <li ui-sref-active="active">
            <a ui-sref="dashboard.home" style="font-size:16px;">
              <span class="glyphicon glyphicon-dashboard"></span>
              &nbsp;&nbsp;首页</a>
          </li>
    
          <li ng-class="{active: true}" ng-repeat="entry in apps | filter: { app: searchApp }">{{dropDown}}
            <a href="javascript:void(0);" ng-click="click($event)" collapse="{{collpaseall == 1}}" style="font-size: 16px;word-break: break-word;">
              &nbsp;{{entry.app}}
              <span class="fa arrow"></span>
              <span class="arrow">({{entry.healthyCount}}/{{entry.machines.length}})</span>
            </a>
    
            <!--<ul class="nav nav-second-level" collapse="{{entry.active}}" style="display: none;">-->
            <ul class="nav nav-second-level" ng-show="entry.active">
              <li ui-sref-active="active">
                <a ui-sref="dashboard.metric({app: entry.app})">
                  <i class="fa fa-bar-chart"></i>&nbsp;&nbsp;实时监控</a>
              </li>
    
              <li ui-sref-active="active" ng-if="!entry.isGateway">
                <a ui-sref="dashboard.identity({app: entry.app})">
                  <i class="glyphicon glyphicon-list-alt"></i>&nbsp;&nbsp;簇点链路</a>
              </li>
    
              <li ui-sref-active="active" ng-if="entry.isGateway">
                <a ui-sref="dashboard.gatewayIdentity({app: entry.app})">
                  <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;请求链路</a>
              </li>
    
              <li ui-sref-active="active" ng-if="entry.appType==0">
                <a ui-sref="dashboard.flow({app: entry.app})">
                  <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则 Nacos</a>
              </li>
    
              <li ui-sref-active="active" ng-if="entry.isGateway">
                <a ui-sref="dashboard.gatewayApi({app: entry.app})">
                  <i class="glyphicon glyphicon-tags"></i>&nbsp;&nbsp;&nbsp;API 管理</a>
              </li>
              <li ui-sref-active="active" ng-if="entry.isGateway">
                <a ui-sref="dashboard.gatewayFlow({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.flowV1({app: entry.app})">
                  <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则 Memory</a>
              </li>
    
              <li ui-sref-active="active">
                <a ui-sref="dashboard.degrade({app: entry.app})">
                  <i class="glyphicon glyphicon-flash"></i>&nbsp;&nbsp;降级规则</a>
              </li>
              <li ui-sref-active="active" ng-if="!entry.isGateway">
                <a ui-sref="dashboard.paramFlow({app: entry.app})">
                  <i class="glyphicon glyphicon-fire"></i>&nbsp;&nbsp;热点规则</a>
              </li>
              <li ui-sref-active="active">
                <a ui-sref="dashboard.system({app: entry.app})">
                  <i class="glyphicon glyphicon-lock"></i>&nbsp;&nbsp;系统规则</a>
              </li>
              <li ui-sref-active="active" ng-if="!entry.isGateway">
                <a ui-sref="dashboard.authority({app: entry.app})">
                  <i class="glyphicon glyphicon-check"></i>&nbsp;&nbsp;授权规则</a>
              </li>
              <li ui-sref-active="active" ng-if="!entry.isGateway">
                <a ui-sref="dashboard.clusterAppServerList({app: entry.app})">
                  <i class="glyphicon glyphicon-cloud"></i>&nbsp;&nbsp;集群流控</a>
              </li>
    
              <li ui-sref-active="active">
                <a ui-sref="dashboard.machine({app: entry.app})">
                  <i class="glyphicon glyphicon-th-list"></i>&nbsp;&nbsp;机器列表</a>
              </li>
            </ul>
            <!-- /.nav-second-level -->
          </li>
        </ul>
      </div>
    </div>

    修改 js webapp => resources => app => scripts => controllers=> identity.js

    FlowServiceV1 改成  FlowServiceV2

    rule 添加 降级规则

    DegradeRuleNacosProvider.java

    /*
     * 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.rule.DegradeRuleEntity;
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity;
    import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
    import com.alibaba.csp.sentinel.datasource.Converter;
    import com.alibaba.csp.sentinel.util.StringUtil;
    import com.alibaba.nacos.api.config.ConfigService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author Eric Zhao
     * @since 1.4.0
     */
    @Component("degradeRuleNacosProvider")
    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);
        }
    }

    DegradeRuleNacosPublisher.java

    /*
     * 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.rule.DegradeRuleEntity;
    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.csp.sentinel.util.AssertUtil;
    import com.alibaba.nacos.api.config.ConfigService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    /**
     * @author Eric Zhao
     * @since 1.4.0
     */
    @Component("degradeRuleNacosPublisher")
    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));
        }
    }

    FlowRuleNacosProvider.java

    /*
     * 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.rule.FlowRuleEntity;
    import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
    import com.alibaba.csp.sentinel.datasource.Converter;
    import com.alibaba.csp.sentinel.util.StringUtil;
    import com.alibaba.nacos.api.config.ConfigService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author Eric Zhao
     * @since 1.4.0
     */
    @Component("flowRuleNacosProvider")
    public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {
    
        @Autowired
        private ConfigService configService;
        @Autowired
        private Converter<String, List<FlowRuleEntity>> converter;
    
        @Override
        public List<FlowRuleEntity> getRules(String appName) throws Exception {
            String rules = configService.getConfig(appName + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, 3000);
            if (StringUtil.isEmpty(rules)) {
                return new ArrayList<>();
            }
            return converter.convert(rules);
        }
    }

    FlowRuleNacosPublisher.java

    /*
     * 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.rule.FlowRuleEntity;
    import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
    import com.alibaba.csp.sentinel.datasource.Converter;
    import com.alibaba.csp.sentinel.util.AssertUtil;
    import com.alibaba.nacos.api.config.ConfigService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    /**
     * @author Eric Zhao
     * @since 1.4.0
     */
    @Component("flowRuleNacosPublisher")
    public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {
    
        @Autowired
        private ConfigService configService;
        @Autowired
        private Converter<List<FlowRuleEntity>, String> converter;
    
        @Override
        public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
            AssertUtil.notEmpty(app, "app name cannot be empty");
            if (rules == null) {
                return;
            }
            configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
                NacosConfigUtil.GROUP_ID, converter.convert(rules));
        }
    }

    NacosConfig.java

    /*
     * 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.rule.DegradeRuleEntity;
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.RuleEntity;
    import com.alibaba.csp.sentinel.datasource.Converter;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.nacos.api.config.ConfigFactory;
    import com.alibaba.nacos.api.config.ConfigService;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.List;
    
    /**
     * @author Eric Zhao
     * @since 1.4.0
     */
    @Configuration
    public class NacosConfig {
    
        @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<DegradeRuleEntity>, String> degradeRuleEntityEncoder() {
            return JSON::toJSONString;
        }
    
        @Bean
        public Converter<String, List<DegradeRuleEntity>> degradeRuleEntityDecoder() {
            return s -> JSON.parseArray(s, DegradeRuleEntity.class);
        }
    
        @Bean
        public ConfigService nacosConfigService() throws Exception {
            return ConfigFactory.createConfigService("localhost");
        }
    }

    NacosConfigUtil.java

    /*
     * 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;
    
    /**
     * @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 DEGRADE_DATA_ID_POSTFIX = "-degrade-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() {}
    }

    DegradeController 添加注入

        @Autowired
        @Qualifier("degradeRuleNacosPublisher")
        private DegradeRuleNacosPublisher degradeRuleNacosPublisher;
    
        @Autowired
        @Qualifier("degradeRuleNacosProvider")
        private DegradeRuleNacosProvider degradeRuleNacosProvider;

    DegradeController  修改接口 如下全文

    /*
     * 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.controller;
    
    import java.util.Date;
    import java.util.List;
    
    import javax.servlet.http.HttpServletRequest;
    
    import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
    import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
    import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
    import com.alibaba.csp.sentinel.dashboard.auth.AuthService.AuthUser;
    import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
    import com.alibaba.csp.sentinel.dashboard.rule.nacos.DegradeRuleNacosProvider;
    import com.alibaba.csp.sentinel.dashboard.rule.nacos.DegradeRuleNacosPublisher;
    import com.alibaba.csp.sentinel.slots.block.RuleConstant;
    import com.alibaba.csp.sentinel.util.StringUtil;
    
    import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
    import com.alibaba.csp.sentinel.dashboard.domain.Result;
    import com.alibaba.csp.sentinel.dashboard.repository.rule.InMemDegradeRuleStore;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.http.MediaType;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    /**
     * @author leyou
     */
    @Controller
    @RequestMapping(value = "/degrade", produces = MediaType.APPLICATION_JSON_VALUE)
    public class DegradeController {
    
        private final Logger logger = LoggerFactory.getLogger(DegradeController.class);
    
        @Autowired
        private InMemDegradeRuleStore repository;
        @Autowired
        private SentinelApiClient sentinelApiClient;
    
        @Autowired
        private AuthService<HttpServletRequest> authService;
    
        @Autowired
        @Qualifier("degradeRuleNacosPublisher")
        private DegradeRuleNacosPublisher degradeRuleNacosPublisher;
    
        @Autowired
        @Qualifier("degradeRuleNacosProvider")
        private DegradeRuleNacosProvider degradeRuleNacosProvider;
    
        @ResponseBody
        @RequestMapping("/rules.json")
        public Result<List<DegradeRuleEntity>> queryMachineRules(HttpServletRequest request, String app, String ip, Integer port) {
            AuthUser authUser = authService.getAuthUser(request);
            authUser.authTarget(app, PrivilegeType.READ_RULE);
    
            if (StringUtil.isEmpty(app)) {
                return Result.ofFail(-1, "app can't be null or empty");
            }
            if (StringUtil.isEmpty(ip)) {
                return Result.ofFail(-1, "ip can't be null or empty");
            }
            if (port == null) {
                return Result.ofFail(-1, "port can't be null");
            }
            try {
                List<DegradeRuleEntity> rules = degradeRuleNacosProvider.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")
        public Result<DegradeRuleEntity> add(HttpServletRequest request,
                                             String app, String ip, Integer port, String limitApp, String resource,
                                             Double count, Integer timeWindow, Integer grade) {
            AuthUser authUser = authService.getAuthUser(request);
            authUser.authTarget(app, PrivilegeType.WRITE_RULE);
    
            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("add error:", throwable);
                return Result.ofThrowable(-1, throwable);
            }
    
            return Result.ofSuccess(entity);
        }
    
        @ResponseBody
        @RequestMapping("/save.json")
        public Result<DegradeRuleEntity> updateIfNotNull(HttpServletRequest request,
                                                         Long id, String app, String limitApp, String resource,
                                                         Double count, Integer timeWindow, Integer grade) {
            AuthUser authUser = authService.getAuthUser(request);
            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");
            }
            authUser.authTarget(entity.getApp(), PrivilegeType.WRITE_RULE);
            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);
                publishRules(entity.getApp());
            } catch (Throwable throwable) {
                logger.error("save error:", throwable);
                return Result.ofThrowable(-1, throwable);
            }
            return Result.ofSuccess(entity);
        }
    
        @ResponseBody
        @RequestMapping("/delete.json")
        public Result<Long> delete(HttpServletRequest request, Long id) {
            AuthUser authUser = authService.getAuthUser(request);
            if (id == null) {
                return Result.ofFail(-1, "id can't be null");
            }
    
            DegradeRuleEntity oldEntity = repository.findById(id);
            if (oldEntity == null) {
                return Result.ofSuccess(null);
            }
            authUser.authTarget(oldEntity.getApp(), PrivilegeType.DELETE_RULE);
            try {
                repository.delete(id);
                publishRules(oldEntity.getApp());
            } catch (Throwable throwable) {
                logger.error("delete error:", throwable);
                return Result.ofThrowable(-1, throwable);
            }
    
            return Result.ofSuccess(id);
        }
    
        private void publishRules(String app) throws Exception{
            List<DegradeRuleEntity> degradeRuleEntities = repository.findAllByApp(app);
            degradeRuleNacosPublisher.publish(app, degradeRuleEntities);
        }
    }

    service-sentinel-nacos 修改配置文件 添加降级配置

    server:
      port: 8081
    spring:
      application:
        name: service-sentinel-nacos
      cloud:
        sentinel:
          transport:
            dashboard: localhost:8080
          datasource:
            flow:
              nacos:
                serverAddr: localhost:8848
                dataId: ${spring.application.name}-flow-rules
                groupId: SENTINEL_GROUP
                ruleType: FLOW
                dataType: json
            degrade:
              nacos:
                serverAddr: localhost:8848
                dataId: ${spring.application.name}-degrade-rules
                groupId: SENTINEL_GROUP
                ruleType: DEGRADE
                dataType: json
    management:
      endpoints:
        web:
          exposure:
            include: '*'
  • 相关阅读:
    05流程图和流程定义的操作
    04启动流程实例,任务的查询与完成
    03流程图的绘制与部署
    02数据库表的初始化方式
    01环境安装
    JavaScript基础和JavaScript内置对象:
    用手机、pid作为win电脑扩展屏
    H5新增特性之语义化标签
    盒模型
    CSS定位总结--static、relative、absolute、fixed
  • 原文地址:https://www.cnblogs.com/lilb/p/14418696.html
Copyright © 2020-2023  润新知