SpringCloud学习之(十九)SpringCloud Alibaba Sentinel实现熔断与限流

文章目录

(十九)SpringCloud Alibaba Sentinel实现熔断与限流

1、Sentinel

1.1 官网

英文:https://github.com/alibaba/Sentinel

中文:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D

1.2 是什么

image-20200827104000917

image-20200827105539477

1.3 去哪下

官方:https://github.com/alibaba/Sentinel/releases

百度网盘:

sentinel-dashboard-1.7.0.jar:

链接:https://pan.baidu.com/s/1xup3p5JI-dVIFQeWDnYxiA

提取码:0rlf

image-20200827104945388

1.4 能干嘛

image-20200827105018758

1.5 怎么玩

https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel

服务使用中的各种问题:

  • 服务雪崩
  • 服务降级
  • 服务熔断
  • 服务限流

2、安装Sentinel控制台

2.1 sentinel组件由2部分组成:

  • 后台
  • 前台8080

image-20200827105941779

2.2 安装步骤

  • 下载:下载到本地sentinel-dashboard-1.7.0.jar

  • 前提:

    • Java8环境OK
    • 8080端口不被占用
  • 命令:

    • java -jar sentinel-dashboard-1.7.0.jar
    • image-20200827110512149
  • 访问sentinel管理界面:

    • http://localhost:8080
    • 登录账号密码均为sentinel

image-20200827110552742

image-20200827110611908

启动Sentinel时报错:

8080端口被占用

Error starting ApplicationContext. To display the conditions report re-run your application with ‘debug’ enabled.
2020-08-27 15:50:48.401 ERROR 15444 — [ main] o.s.b.d.LoggingFailureAnalysisReporter :


APPLICATION FAILED TO START


Description:

The Tomcat connector configured to listen on port 8080 failed to start. The port may already be in use or the connector may be misconfigured.

Action:

Verify the connector’s configuration, identify and stop any process that’s listening on port 8080, or configure this application to listen on another port.

2020-08-27 15:50:48.402 INFO 15444 — [ main] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@61baa894: startup date [Thu Aug 27 15:50:42 CST 2020]; root of context hierarchy
2020-08-27 15:50:48.408 INFO 15444 — [ main] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown

解决:

Microsoft Windows [版本 10.0.18363.1016]
© 2019 Microsoft Corporation。保留所有权利。

C:\Users\17316>netstat -ano | findstr “8080”
TCP 0.0.0.0:8080 0.0.0.0:0 LISTENING 16036
TCP [::]:8080 [::]:0 LISTENING 16036
TCP [::1]:2558 [::1]:8080 TIME_WAIT 0
TCP [::1]:2559 [::1]:8080 TIME_WAIT 0

C:\Users\17316>tasklist | findstr “16036”
java.exe 16036 Console 1 364,076 K

C:\Users\17316>taskkill /f /t /im java.exe
成功: 已终止 PID 12280 (属于 PID 11280 子进程)的进程。
成功: 已终止 PID 11576 (属于 PID 12944 子进程)的进程。
成功: 已终止 PID 364 (属于 PID 16036 子进程)的进程。
成功: 已终止 PID 12892 (属于 PID 13780 子进程)的进程。
成功: 已终止 PID 11280 (属于 PID 10028 子进程)的进程。
成功: 已终止 PID 12944 (属于 PID 11052 子进程)的进程。
成功: 已终止 PID 15964 (属于 PID 16128 子进程)的进程。
成功: 已终止 PID 16036 (属于 PID 10028 子进程)的进程。
成功: 已终止 PID 13780 (属于 PID 10028 子进程)的进程。
成功: 已终止 PID 15444 (属于 PID 7692 子进程)的进程。

C:\Users\17316>

再次启动Sentinel:

image-20200827160213407

3、初始化演示工程

3.1 启动Nacos8848成功

http://localhost:8848/nacos/#/login

3.2 Module

3.2.1 新建model

cloudalibaba-sentinel-service8401

3.2.2 POM
 <dependencies>

        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

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

        <!--sentinel-datasource-nacos:后序做持久化会用到-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

        <!--spring-cloud-starter-alibaba-sentinel-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

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

        <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>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>4.6.3</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>
3.2.3 YML

application.yml

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        #Nacos服务注册地址
        server-addr: localhost:8848
    sentinel:
      transport:
        #配置Sentinel dashboard地址
        dashboard: localhost:8080
        port: 8719  #默认8719,假如被占用了会自动从8719开始依次+1扫描。直至找到未被占用的端口

management:
  endpoints:
    web:
      exposure:
        include: '*'
3.2.4 主启动类
package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class SentinelServiceMain8401 {
    public static void main(String[] args) {
        SpringApplication.run(SentinelServiceMain8401.class,args);
    }
}
3.2.5 业务类

FlowLimitController.java

package com.atguigu.springcloud.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
//流控
public class FlowLimitController {
    @GetMapping("/testA")
    public String testA() {
        return "------testA";
    }

    @GetMapping("/testB")
    public String testB() {

        return "------testB";
    }
}
3.2.6 启动Sentinel8080

java -jar sentinel-dashboard-1.7.0

3.2.7 启动微服务8401
  • 启动8401微服务后查看sentienl控制台

  • 空空如也

  • Sentinel采用的懒加载说明

  • 执行一次访问:

    • http://localhost:8401/testA
    • http://localhost:8401/testB
  • 效果:

    image-20200827212302158

  • 结论:

    sentinel8080正在监控微服务8401

3.2.8 遇到的错误:

2020-08-27 15:49:02.625  INFO 16036 --- [5)-192.168.47.1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 25 ms
INFO: log base dir is: C:\Users\17316\logs\csp\
INFO: log name use pid is: false
2020-08-27 15:49:03.888 ERROR 16036 --- [7)-192.168.47.1] c.a.c.n.discovery.NacosDiscoveryClient   : get service name from nacos server fail,

java.lang.IllegalArgumentException: no server available
	at com.alibaba.nacos.client.naming.net.NamingProxy.reqAPI(NamingProxy.java:438) ~[nacos-client-1.1.1.jar:na]

查看:是application.yml写错了

image-20200827164412002

4、流控规则

4.1 基本介绍

添加流控规则:

image-20200828035559537

进一步解释说明:

image-20200828035731157

image-20200828035804808

4.2 流控模式

4.2.1 直接(默认)

直接–》就是快速失败(系统默认)

测试:

首先对testA新建一个流控规则,单机阈值为:1,其他的为默认

image-20200828040447923

当间隔一秒访问testA,正常输出:------testA

当快速访问testA(小于1秒),显示:Blocked by Sentinel (flow limiting)

被Sentinel限流

思考:直接调用默认报错信息,技术方面OK but,是否应该有我们自己的后续处理?

类似有一个fallback的兜底方法?

4.2.2 关联

1)是什么

  • 当关联的资源达到阈值时,就限流自己

  • 当与A关联的资源B达到阈值后,就限流自己

  • B惹事,A挂了

2)配置A

image-20200828042541437

3)postman模拟并发密集访问testB

  • 访问testB成功

image-20200828042848276

  • postman里新建多线程集合组

image-20200828043521436

  • 将访问地址添加进新线程组

image-20200828043608178

  • Run

    image-20200828043659412

image-20200828043812472

运行:testB

image-20200828043854664

这时再去访问testA,会出现限流

大批量线程高并发访问B,导致A失效了

image-20200828043946044

当testB访问结束,再次访问testA正常

4.2.3 链路

https://blog.csdn.net/ooyhao/article/details/102745914

4.3 流控效果

4.3.1 直接-——》快速失败(默认的流控处理)
  • 直接失败,抛异常:Blocked by Sentinel (flow limiting)
  • 源码:com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
4.3.2 预热

1)说明

公式:阈值除以coldFactor(默认值为3),经过预热时长后才会达到阈值

2)官网

image-20200828094210991

默认coldFactor为3,即请求QPS从threshold/3开始,经预热时长逐渐升至设定的QPS阈值。

限流 冷启动:https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6

3)源码

com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController

image-20200828094519782

4)Warmup配置

image-20200828094613442

5)多次点击http://localhost:8401/testB

刚开始不行,后续慢慢OK

6)应用场景
如:秒杀系统在开启的瞬间,会有很多流量上来,很有可能把系统打死,预热方式就是把为了保护系统,慢慢的把阀值增长到设置的阀值

4.3.3 排队等待

image-20200828095349094

1)匀速排队,阈值必须设置为QPS

2)官网

image-20200828095530223

3)源码

com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController

4)测试

image-20200828100603820

编辑FlowLimitController.java

@GetMapping("/testB")
public String testB() {
    System.out.println(Thread.currentThread().getName()+"\t"+"....testB");
    return "------testB";
}

编辑postman,并启动

image-20200828101140263

查看8401后台:

image-20200828101226116

5、降级规则

5.1 官网

https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7

5.2 基本介绍

image-20200828102102387

image-20200828102118817

1)进一步说明:

Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。
当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException),

2)Sentinel的断路器是没有半开状态的

半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用。具体可以参考Hystrix

image-20200828102426230

5.3 降级策略实战

5.3.1 RT
1)概念

平均响应时间 (DEGRADE_GRADE_RT):当 1s 内持续进入 5 个请求,对应时刻的平均响应时间(秒级)均超过阈值(count,以 ms 为单位),那么在接下的时间窗口(DegradeRule 中的 timeWindow,以 s 为单位)之内,对这个方法的调用都会自动地熔断(抛出 DegradeException)。注意 Sentinel 默认统计的 RT 上限是 4900 ms,超出此阈值的都会算作 4900 ms,若需要变更此上限可以通过启动配置项 -Dcsp.sentinel.statistic.max.rt=xxx 来配置。

image-20200828103146404

2)测试

代码:

//测试RT
@GetMapping("/testD")
public String testD()
{
    try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
    System.out.println("testD 测试RT");
    return "------testD";
}

配置降级规则:

image-20200828103809493

jmeter压测:

image-20200828110550188

测试:

  • JMeter压测前测试:

image-20200828110657546

虽然平均响应时间超过200ms,但是并发量只是1,没有超过5,所以没有触发熔断降级。

  • JMeter压测测试:

    1秒10个线程,同时访问testD,触发熔断降级

  • 开启压测后:

    永远一秒钟打进来10个线程(大于5个)调用/testD,我们希望200ms处理完本次任务,如果200ms还没处理完,在之后1s时间窗口内,断路器打开,微服务不可用。

3)总结:

image-20200828110129416

image-20200828110141788

5.3.2 异常比例
1)概念

异常比例( DEGRADE GRADE_ EXCEPTION_ RATIO):

​ 当资源的每秒请求量>= 5,并且每秒异常总数占通过量的比值超过阈值( DegradeRule中

的count )之后,资源进入降级状态,即在接下的时间窗口( DegradeRule中timeWindow, 以s为单位)之内,对这个方法的调用都会自动地返回。异常比率的阈值范围是[0.0, 1.0] ,代表0% - 100%。

image-20200828145719073

2)测试

代码:

@GetMapping("/testE")
public String testE()
{
    System.out.println("testE   测试异常比例");
    //模拟出错:每访问一次就是100%的错
    int age = 10/0;
    return "------testE";
}

配置:

image-20200828150955483

JMeter:

image-20200828150222106

启动JMeter后访问http://localhost:8401/testE,显示Blocked BySentenel(flow limitung),服务降级了

思考:

异常比例的概念:当资源的每秒请求量>= 5,并且每秒异常总数占通过量的比值超过阈值( DegradeRule中的count )之后,资源进入降级状态,

我们通过JMeter模拟,1秒访问的线程数为20个,并且每秒异常总数占通过量的比值为100%

上数条件都满足异常比例的概念,服务将会进入降级状态

停止JMEter后,直接报错:

停止后,不符合【每秒请求量>=5 】的条件,所以会直接报错

image-20200828151242415

结论:

image-20200828150250040

5.3.3 异常数
1)概念

异常数( DEGRADE _GRADE_ EXCEPTION_ COUNT):

当资源近1分钟的异常数目超过阈值之后会进行熔断。注意由于统计时间窗口是分钟级别的,若timevindow 小于60s,则结束熔断状态后仍可能再进入熔断状态。所以时间窗口必须大于或等于60s。

image-20200828152145282

2)异常数是按照分钟统计的
3)测试

代码:

@GetMapping("/testF")
public String testF()
{
    System.out.println("testF   测试异常数");
    int age = 10/0;
    return "------testF";
}

配置:

image-20200828152644491

访问:http://localhost:8401/testE,第一次访问绝对报错,因为除数不能为零,

我们看到error窗口,但是达到5次报错后,进入熔断后降级。

image-20200828152815984

6、热点key限流

6.1 基本介绍

image-20200828203047857

6.2 官网

https://github.com/alibaba/Sentinel/wiki/热点参数限流

6.3 承上启下复习start

兜底方法

分为系统默认和客户自定义两种

之前的case,限流出问题后,都是用sentine系统默认的提示: Blocked by Sentinel (flow limiting)

我们能不能自定?类似hystrix,某个方法出问题了,就找对应的兜底降级方法?

结论:从HystrixCommand@SentinelResource

6.4 代码

com.alibaba.csp.sentinel.slots.block.BlockException

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                         @RequestParam(value = "p2",required = false) String p2) {
    //int age = 10/0;
    return "------testHotKey";
}
 
//兜底方法
public String deal_testHotKey (String p1, String p2, BlockException exception){
    return "------deal_testHotKey,o(╥﹏╥)o";  
}

启动8401,测试

http://localhost:8401/testHotKey

http://localhost:8401/testHotKey?p1=aaa

http://localhost:8401/testHotKey?p1=aaa&p2=bbb

都能正常访问

image-20200828204423486

6.5 配置

image-20200828204924023

  • 没有配置blockHandler:@SentinelResource(value = “testHotKey”)
    • 异常打到了前台用户界面看不到,不友好
  • 配置了blockHandler:@SentinelResource(value = “testHotKey”,blockHandler = “deal_testHotKey”)
    • 方法testHostKey里面第一个参数只要QPS超过每秒1次,马上降级处理
    • 用了我们自己定义的

image-20200828210221127

6.6 测试

http://localhost:8401/testHotKey

http://localhost:8401/testHotKey?p1=aaa error

http://localhost:8401/testHotKey?p1=aaa&p2=bbb error

http://localhost:8401/testHotKey?p2=bbb right

只要个p1传递参数的都会限流

6.7 参数例外项

上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流

6.7.1 特殊情况

普通:超过1秒钟一个后,达到阈值1后马上被限流

我们期望p1参数当它是某个特殊值时,它的限流值和平时不一样

特例:假如当p1的值等于5时,它的阈值可以达到200

配置:

image-20200828212144649

添加按钮不能忘

6.7.2 测试:

http://localhost:8401/testHotKey?p1=5

http://localhost:8401/testHotKey?p1=3

当p1等于5的时候,阈值变为200

当p1不等于5的时候,阈值就是平常的1

6.7.3 前提条件

热点参数的注意点,参数必须是基本类型或者String

6.7.4 其他

添加异常看看:

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
                         @RequestParam(value = "p2",required = false) String p2){
    int age = 10/0;
    return "------testHotKey";
}
//兜底的方法
public String deal_testHotKey(String p1, String p2, BlockException exception){
    return "------deal_testHotKey,o(╥﹏╥)o";
}

image-20200828214113019

7、系统规则

7.1 是什么

https://github.com/alibaba/Sentinel/wiki/系统自适应限流

7.2 各项配置参数说明

image-20200828214411980

7.3 配置全局QPS

image-20200828222848394

8、@SentinelResource

8.1按资源名称限流+后续处理

8.1.1 启动Nacos成功
8.1.2 启动Sentinel成功
8.1.3 添加业务类
package com.atguigu.springcloud.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RateLimitController {

    @GetMapping("/byResource")
    @SentinelResource(value = "byResource",blockHandler = "handleException")
    public CommonResult byResource(){
        return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));
    }

    public CommonResult handleException(BlockException exception){
        return new CommonResult(444,exception.getClass().getCanonicalName()+"\t服务不可用");
    }
}
8.1.4 配置流控规则

配置步骤:

image-20200828230747763

表示1秒钟内查询次数大于1,就跑到我们自定义的处流,限流

多次点击:

image-20200828231418170

测试:

1秒钟点击1下,OK

超过上述问题,疯狂点击,返回了自己定义的限流处理信息,限流发送

8.1.5 额外问题

此时关闭微服务8401看看

Sentinel控制台,流控规则消失了?????

临时/持久? 临时

8.2按照Url地址限流+后续处理

  • 通过访问的URL来限流,会返回Sentinel自带默认的限流处理信息

  • 业务类RateLimitController

    //按url限流测试OK
        @GetMapping("/rateLimit/byUrl")
        @SentinelResource(value = "byUrl") // 没有兜底的方法,会采用系统默认的
        public CommonResult byUrl()
        {
            return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002"));
        }
    
  • 访问http://localhost:8401/rateLimit/byUrl

    image-20200829101842735

  • Sentinel控制台配置

    image-20200829101950819

image-20200829101641413

  • 测试

    疯狂点击http://localhost:8401/rateLimit/byUrl

    image-20200829102032650

8.3 上面兜底方法面临的问题

  1. 系统默认的,没有体现我们自己的业务要求。
  2. 依照现有条件,我们自定义的处理方法又和业务代码耦合在一块,不直观。
  3. 每个业务方法都添加一个兜底的,那代码膨胀加剧。
  4. 全局统一的处理方法没有体现。

8.4 客户自定义限流处理逻辑

  • 创建customerBlockHandler类用于自定义限流处理逻辑

  • 自定义限流处理类:CustomerBlockHandler.java

    package com.atguigu.springcloud.myhandler;
    
    import com.atguigu.springcloud.entities.CommonResult;
    import com.atguigu.springcloud.entities.Payment;
    
    //统一处理的兜底方法
    public class CustomerBlockHandler {
        //兜底方法1
        public static CommonResult handleException1(){
            return new CommonResult(4444,"Customers自定义限流处理信息—--1");
        }
    
        //兜底方法2
        public static CommonResult handleException2(){
            return new CommonResult(4444,"Customers自定义限流处理信息—--2");
        }
    
    }
    
  • RateLimitController.java

    //按照客户自定义  全局
    @GetMapping("/rateLimit/customerBlockHandler")
    @SentinelResource(value = "customerBlockHandler",
            blockHandlerClass = CustomerBlockHandler.class,
            blockHandler = "handleException1")
    public CommonResult customerBlockHandler(){
        return new CommonResult(200,"按照客户自定义",new Payment(2020L,"serial002"));
    }
    
  • 启动微服务后先调用一次

    http://localhost:8401/rateLimit/customerBlockHandler

image-20200829104605542

  • Sentinel控制台配置

image-20200829105506122

image-20200829105552358

  • 再次访问

    image-20200829110053469

可能遇到的问题:

  1. 添加流控规则时按照URL进行流控,当再次以QPS每秒大于1 访问http://localhost:8401/rateLimit/customerBlockHandler,会触发系统默认限流
  2. 当修改流控规则按照资源名进行流控,当再次以QPS每秒大于1 访问,照样还是系统默认的限流方式
  3. 解决办法:删除按照URL限流的规则

image-20200829110529124

进一步说明:

image-20200829111016780

8.5 更多注解属性说明

image-20200829111230423

image-20200829111256826

Sentinel主要有三个核心API

  • SphU定义资源
  • Tracer定义统计
  • ContextUtil定义了上下文

9、服务熔断功能

sentinel整合ribbon+openFeign+fallback

9.1 Ribbon系列

9.1.1 启动nacos和sentinel
9.1.2 提供者9003/9004

1)新建cloudalibaba-consumer-nacos-order84

2)POM

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <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>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

3)YML

记得修改端口

server:
  port: 9003

spring:
  application:
    name: nacos-payment-provider
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #配置Nacos地址

management:
  endpoints:
    web:
      exposure:
        include: '*'

4)主启动

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;


@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003
{
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain9003.class, args);
    }
}

5)业务类

package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;

@RestController
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;

    //模拟数据库
    public static HashMap<Long, Payment> hashMap = new HashMap<>();
    static{
        hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181"));
        hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));
        hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
    }

    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
        Payment payment = hashMap.get(id);
        CommonResult<Payment> result = new CommonResult(200,"from mysql,serverPort:  "+serverPort,payment);
        return result;
    }

}

6)测试

http://localhost:9003/paymentSQL/1

http://localhost:9004/paymentSQL/1

都正常

image-20200829150813588

9.1.3 消费者84
1)新建model:cloudalibaba-consumer-nacos-order84
2)POM
  <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <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>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
3)YML
server:
  port: 84


spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719

service-url:
  nacos-user-service: http://nacos-payment-provider
4)主启动类
package com.atguigu.spring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class OrderNacosMain84
{
    public static void main(String[] args) {
        SpringApplication.run(OrderNacosMain84.class, args);
    }
}
5)业务类
  1. ApplicationContextConfig.java
package com.atguigu.spring.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;


@Configuration
public class ApplicationContextConfig
{
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate()
    {
        return new RestTemplate();
    }
}
  1. CircleBreakerController.java
package com.atguigu.spring.controller;
...

@RestController
public class CircleBreakerController {
    public static final String SERVER_URL = "http://nacos-payment-provider";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/consumer/fallback/{id}")
    @SentinelResource(value = "fallback")
    public CommonResult<Payment> fallback(@PathVariable("id") Long id){
        CommonResult result = restTemplate.getForObject(SERVER_URL + "/paymentSQL/" + id, CommonResult.class, id);

        if(id == 4){
            throw new IllegalArgumentException("IllegalArgumentException ,非法参数异常");
        }else if (result.getData() == null){
            throw new NullPointerException("NullPointerException ,该ID没有对应的记录,空指针异常");
        }

        return result;
    }
}
6)启动测试

测试1:数据库存在

image-20200829151314577

image-20200829152715452

9003和9004轮询

测试2:数据库中不存在,但对id=4,有处理

image-20200829151401752

测试3:数据库中不存在,id也不是4的其他请求有处理

image-20200829151442294

给客户error页面,不友好

所以修改代码,添加兜底方法

7)@SentinelResource中的fallback与blockHandler
第一种情况:

上面的代码为

第一种情况:没有配置

@SentinelResource(value = “fallback”)

第二种情况:只配置fallback

第二种情况:只配置fallback
@SentinelResource(value = “fallback” , fallback = “handlerFallback”)//fallback :只负责业务异常

	@GetMapping("/consumer/fallback/{id}")
    //第二种情况:
    @SentinelResource(value = "fallback" , fallback = "handlerFallback")//fallback  :只负责业务异常
public CommonResult<Payment> fallback(@PathVariable("id") Long id){
    CommonResult result = restTemplate.getForObject(SERVER_URL + "/paymentSQL/" + id, CommonResult.class, id);

    if(id == 4){
        throw new IllegalArgumentException("IllegalArgumentException ,非法参数异常");
    }else if (result.getData() == null){
        throw new NullPointerException("NullPointerException ,该ID没有对应的记录,空指针异常");
    }

    return result;
}
//handlerFallback
public CommonResult handlerFallback(@PathVariable Long id,Throwable e){
    Payment payment = new Payment(id, "null");
    return new CommonResult(444,"兜底的异常handlerFallback,exception异常的内容:"+e.getMessage(),payment);
}

重启消费者84:

测试:http://localhost:84/consumer/fallback/4

image-20200829153447334

http://localhost:84/consumer/fallback/5

image-20200829153339262

第三种情况:只配置blockHandler
//第三种情况:
	@SentinelResource(value = "fallback" ,blockHandler = "blockHandler")//解决sentinel配置异常
	。。。。。。。。。。。。。。。。。。。。。

   
    //blockHandler 
    public CommonResult blockHandler(@PathVariable  Long id, BlockException blockException) {
        Payment payment = new Payment(id,"null");
        return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);
    }

配置sentinel:

image-20200829154932720

image-20200829154750036

测试:

点击一次:localhost:84/consumer/fallback/4 出现非法参数异常

image-20200829155004682

连续点击:

image-20200829155110984

所以当连续点击触发了sentinel配置异常,会调用blockHandler中兜底方法

第四种情况:fallback和blockHandler都配置
//第四种情况:
@SentinelResource(value = "fallback" ,fallback = "handlerFallback" ,blockHandler = "blockHandler")
public CommonResult<Payment> fallback(@PathVariable("id") Long id){
    CommonResult result = restTemplate.getForObject(SERVER_URL + "/paymentSQL/" + id, CommonResult.class, id);

    if(id == 4){
        throw new IllegalArgumentException("IllegalArgumentException ,非法参数异常");
    }else if (result.getData() == null){
        throw new NullPointerException("NullPointerException ,该ID没有对应的记录,空指针异常");
    }

    return result;
}
//fallback
public CommonResult handlerFallback(@PathVariable Long id,Throwable e){
    Payment payment = new Payment(id, "null");
    return new CommonResult(444,"兜底的异常handlerFallback,exception异常的内容:"+e.getMessage(),payment);
}

//blockHandler
public CommonResult blockHandler(@PathVariable  Long id, BlockException blockException) {
    Payment payment = new Payment(id,"null");
    return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException  "+blockException.getMessage(),payment);
}

重启84,然后在sentinel添加流控规则:

image-20200829160042484

访问1:

  • http://localhost:84/consumer/fallback/1 点击一次

    image-20200829160132947

  • http://localhost:84/consumer/fallback/1 连续点击多次

    image-20200829160214136

访问2:

  • http://localhost:84/consumer/fallback/4 点击一次

    image-20200829160315887

  • http://localhost:84/consumer/fallback/4 连续点击多次

    image-20200829160350123

总结:若blockHandler和fallback都进行了配置,则被限流降级而抛出BlockException 时只会进入blockHandler处理逻辑。

8)忽略属性…

exceptionsToIgnore

image-20200829160808463

image-20200829160847001

9.2 Feign系列

9.2.1 修改84模块
9.2.2 POM
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
9.2.3 YML
#对Feign的支持
feign:
  sentinel:
    enabled: true
9.2.4 业务类

带@FeignClient注解的业务接口

package com.atguigu.spring.service;


import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService
{
    //84调用9003
    @GetMapping(value = "/paymentSQL/{id}")
    public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}

PaymentFallbackService实现类

package com.atguigu.spring.service;

import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import org.springframework.stereotype.Component;

@Component
public class PaymentFallbackService implements PaymentService{
    @Override
    public CommonResult<Payment> paymentSQL(Long id) {
        return new CommonResult<>(44444,"服务降级返回,---PaymentFallbackService",new Payment(id,"errorSerial"));

    }
}

Controller

//=====================openFeign==================
@Resource
private PaymentService paymentService;
@GetMapping(value = "/consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id){
    return paymentService.paymentSQL(id);
}
9.2.5 主启动

添加@EnableFeignClients启动Feign的功能

9.2.6 测试

http://localhost:84/consumer/paymentSQL/1

测试84调用9003,此时故意关闭9003微服务提供者,看84消费侧自动降级,不会被耗死

image-20200830164450214

9.3 熔断框架比较

image-20200830164558049

10、规则持久化

10.1 是什么

一旦我们重启应用,Sentinel规则将消失,生产环境需要将配置规则进行持久化

10.2 怎么玩

将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上Sentinel上的流控规则持续有效

10.3 步骤

修改cloudalibaba-sentinel-service8401

POM

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

YML

server:
  port: 8401

spring:
  application:
    name: cloudalibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
    sentinel:
      transport:
        dashboard: localhost:8080 #配置Sentinel dashboard地址
        port: 8719
      # 添加Nacos数据源配置
      datasource:
        #数据源1
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: cloudalibaba-sentinel-service
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow #流控规则

management:
  endpoints:
    web:
      exposure:
        include: '*'

feign:
  sentinel:
    enabled: true # 激活Sentinel对Feign的支持

添加Nacos业务规则配置

image-20200830172052555

image-20200830172100800

内容解析:

[
    {
         "resource": "/retaLimit/byUrl",
         "limitApp": "default",
         "grade":   1,
         "count":   1,
         "strategy": 0,
         "controlBehavior": 0,
         "clusterMode": false    
    }
]

image-20200830172135442

启动8401后刷新sentinel发现业务规则有了

image-20200830172229435

快速访问测试接口:http://localhost:8401/rataLimit/byUrl

image-20200830172337638

停止8401再看sentinel

image-20200830172427922

重新启动8401再看sentinel:

等一会之后,再查看

image-20200830172652805

再次访问:http://localhost:8401/rataLimit/byUrl

重新配置出现了,持久化验证通过

查看配置存在了那:存在了数据库中

image-20200830173002117

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值