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\n";
    }
}
集成 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: '*'

 

posted @ 2021-02-20 01:05  Goosander  阅读(190)  评论(0编辑  收藏  举报