SpringCloudAlibaba 学习第二节 Sentinel第二节

本文详细介绍了Sentinel服务降级、熔断的策略,包括慢调用比例、异常比例和异常数,并展示了如何配置降级规则。此外,还讲解了热点参数限流,通过示例展示了如何针对热点参数进行限流。最后,探讨了系统自适应限流,包括Load、CPU usage、平均RT和并发线程数四种模式,以及如何结合Feign进行服务降级。
摘要由CSDN通过智能技术生成

sentinel降级规则

概述

    详细参考官网说明地址
    除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。在微服务项目中,一个服务常常会调用别的微服务模块,微服务提供方的稳定性不可控,如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
     简单调用链路 如下图

调用
微服务A
微服务B
微服务C
微服务D
微服务E
微服务F

    不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。

熔断策略

    Sentinel 1.8.0 版本对熔断降级特性进行了全新的改进升级

    1、慢调用比例 (SLOW_REQUEST_RATIO):
选择以慢调用比例作为阈值,需要设置允许的慢调用RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN
状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
    2、异常比例
(ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN
状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% -
100%。
    3、异常数
(ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
    

熔断降级规则说明
熔断降级规则(DegradeRule)包含下面几个重要的属性:

Field说明默认值
resource资源名,即规则的作用对象
grade熔断策略,支持慢调用比例/异常比例/异常数策略慢调用比例
count慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值
timeWindow熔断时长,单位为 s
minRequestAmount熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入)5
statIntervalMs统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入)1000 ms
slowRatioThreshold慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入

    Sentinel相关的jar包升级至1.8.0,不然Sentinel客户端使用1.8.0,但是与项目中版本不一致,可能有各种不可控的意外发生。
    慢调用比例 (SLOW_REQUEST_RATIO)
    8401 项目升级jar包,controller 添加方法 延时1秒返回

@GetMapping("/fusing")
    public String fusingRT() {
        try {
            TimeUnit.SECONDS.sleep(1L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "测试RT";
    }

    Sentinel降级规则设置如下,大致意思是在单位统计时长内(默认1s)超过了最小请求数5次,慢调用时长超过200微秒且慢调用比例超过阈值50%,将会触发熔断,熔断时长是10s,超过熔断时长后,将会处于半开状态,尝试恢复调用,下次请求响应时长依旧超过200微秒,继续熔断。
在这里插入图片描述
    测试 使用 jmeter 压力测试,程序进入熔断状态
在这里插入图片描述
在这里插入图片描述
     经过熔断时长后,再次请求会通过一次,之后会继续熔断
在这里插入图片描述
    异常比例
    controller 添加代码方法fusing-a

	@GetMapping("/fusing-a")
    public String fusingRTA() {
        Random random = new Random();
        int i = random.nextInt(100) + 1;
        if (i % 2 == 0) {
            throw new RuntimeException(LocalDateTime.now() + "\t程序运行出错");
        }
        return "测试RT异常比例 OK\t" + LocalDateTime.now();
    }

    sentinel配置,大致意思是在单位统计时长内(默认1s)超过了最小请求数5次,出现异常的比例超过设置的阈值20%,将会触发熔断,熔断时长是10s,超过熔断时长后,将会处于半开状态,尝试恢复调用,下次调用出现异常,继续熔断,下次调用正常则恢复链路调用,只有手速快,不需要使用jmeter压力测试。
在这里插入图片描述
在这里插入图片描述
    异常数
    配置,大致意思是在单位统计时长内(默认1s)超过了最小请求数5次,出现异常次数大于2,将会触发熔断,熔断时长是10s,超过熔断时长后,将会处于半开状态,尝试恢复调用,下次调用出现异常,继续熔断,下次调用正常则恢复链路调用。
在这里插入图片描述
在这里插入图片描述

热点参数限流

    详细解释参考官方文档

    何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
    商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
    用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
    热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源, 调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
    Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。

param a
param b
param c
热点 QPS > 5 限流
热点 QPS <= 5 不限流
非热点 QPS > 5 不限流
rest 请求
sentinel 热点限流,QPS是5
请求进入下一个链路

在这里插入图片描述

     8401模块controller 添加代码

@GetMapping("/hot-key")
    // value name of the Sentinel resource Sentinel资源的名称 必须是唯一的
    // name of the block exception function, empty by default 违背Sentinel配置将会执行的备用方法
    @SentinelResource(value = "sentinel-hot-key", blockHandler = "fusingHotKey")
    public String hotKey(@RequestParam(value = "k1", required = false) String k1,
     					 @RequestParam(value = "k2", required = false) String k2) {
        return "k1 ---> hotKey";
    }

    public String fusingHotKey(String k1, String k2, BlockException e) {
        return "k1--->hotKey ---> 被限流";
    }

     Sentinel 热点key配置。大致意思是 sentinel-hot-key资源,对第一个参数,也就是k1设置为热点参数,该参数不为空且在统计时间窗口10s内,请求QPS阈值超过2,将会被限流,但是当k1=vip限流阈值是200。
在这里插入图片描述
    测试
在这里插入图片描述

系统自适应限流

    官网详细介绍资料

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

@SentinelResource

    官方详细解释

    value:资源名称,必需项(不能为空)
    entryType:entry 类型,可选项(默认为 EntryType.OUT)
    blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
    fallback / fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:1、返回值类型必须与原函数返回值类型一致;2、方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。3、fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
    defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:1、返回值类型必须与原函数返回值类型一致;2、方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。3、defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
    exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。

    在8401中添加代码

package com.zjt.cloud.controller;

import cn.hutool.core.lang.UUID;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.zjt.cloud.domain.CommonResult;
import com.zjt.cloud.domain.Payment;
import com.zjt.cloud.handler.MineBlockHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author zjt
 * @date 2020-10-22
 */
@Slf4j
@RestController
@RequestMapping("/sentinel-limit")
public class SentinelLimitController {

    @GetMapping("/get-payment")
    @SentinelResource(value = "sentinel-limit-get-payment", blockHandler = "fusingGetPayment")
    public CommonResult<Payment> getPayment() {
        return new CommonResult<>(200, "Sentinel 限流测试成功", new Payment(10010L, UUID.fastUUID().toString()));
    }

    public CommonResult<Payment> fusingGetPayment(BlockException e) {
        log.info(e.getMessage());
        return new CommonResult<>(500, e.getClass().getCanonicalName() + "\t 服务不可用");
    }

    @GetMapping("/custom")
    @SentinelResource(value = "custom-block-handler",
            blockHandlerClass = MineBlockHandler.class,
            blockHandler = "handlerBlockException")
    public CommonResult<Payment> customBlockHandler(Integer id) {
        if (id == 5) {
            throw new ArithmeticException("id错误");
        }
        return new CommonResult<>(200, " success", new Payment(Long.valueOf(id), UUID.fastUUID().toString()));
    }
}

package com.zjt.cloud.handler;

import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.zjt.cloud.domain.CommonResult;
import com.zjt.cloud.domain.Payment;
import lombok.extern.slf4j.Slf4j;

/**
 * @author zjt
 * @date 2020-10-22
 */
@Slf4j
public class MineBlockHandler {

    public static CommonResult<Payment> handlerBlockException(BlockException e) {
        return new CommonResult<>(500, "自定义限流处理");
    }
	// 仅接收 BlockException 无法处理其他业务异常
    public static CommonResult<Payment> handlerBlockException(Integer id, BlockException e) {
        log.info("id->" + id);
        log.info(e.getMessage());
        return new CommonResult<>(500, "自定义限流处理->id" + id);
    }
    
}

    sentinel配置流控规则和降级规则
在这里插入图片描述
在这里插入图片描述
    测试
在这里插入图片描述
在这里插入图片描述
    sentinel 结合ribbon 结合 openfeign 使用,新建9003 9004 作为服务提供者,新建84模块作为服务提供者。9003模块
    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">
    <parent>
        <artifactId>zjt-cloud-api</artifactId>
        <groupId>com.zjt.cloud-api</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloudalibaba-provider-payment9003</artifactId>

    <dependencies>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.zjt.cloud-api</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <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>
        <!--日常通用jar包配置-->
        <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>
</project>

    application.yml

server:
  port: 9003

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

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

    主启动类

package com.zjt.cloud;

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

import java.util.TimeZone;

/**
 * @author zjt
 * @date 2020-10-22
 */
@SpringBootApplication
@EnableDiscoveryClient
public class CloudAlibabaPayment9003Application {

    public static void main(String[] args) {
        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
        SpringApplication.run(CloudAlibabaPayment9003Application.class, args);
    }

}

    业务类

package com.zjt.cloud.controller;

import com.zjt.cloud.domain.CommonResult;
import com.zjt.cloud.domain.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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.LinkedHashMap;

/**
 * @author zjt
 * @date 2020-10-22
 */
@RestController
@RequestMapping("/payment")
public class PaymentProviderController {

    public static HashMap<Long, Payment> map = new LinkedHashMap<Long, Payment>() {{
        put(1L, new Payment(1L, "value1"));
        put(2L, new Payment(2L, "value2"));
        put(3L, new Payment(3L, "value3"));
    }};

    @Value("${server.port}")
    private String port;

    @GetMapping("/{id}")
    public CommonResult<Payment> getPayment(@PathVariable Long id) {
        if (id == 8) {
            throw new IllegalArgumentException("参数异常");
        }
        Payment payment = map.get(id);
        return new CommonResult<>(200, "success from port" + port, payment);
    }

}

    84消费者模块
    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">
    <parent>
        <artifactId>zjt-cloud-api</artifactId>
        <groupId>com.zjt.cloud-api</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloudalibaba-provider-payment9004</artifactId>

    <dependencies>
        <!--SpringCloud ailibaba nacos -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.zjt.cloud-api</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <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>
        <!--日常通用jar包配置-->
        <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>

</project>

    application.yml

server:
  port: 84


spring:
  application:
    name: nacos-order-consumer
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        #配置Sentinel dashboard地址
        dashboard: localhost:10010
        #默认8719端口,假如被占用会自动从8719开始依次+1扫描,直至找到未被占用的端口
        port: 8719

#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
  nacos-user-service: http://nacos-payment-provider

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

    主启动类

package com.zjt.cloud;

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

import java.util.TimeZone;

/**
 * @author zjt
 * @date 2020-10-22
 */
@EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
public class CloudAlibabaConsumer84Application {

    public static void main(String[] args) {
        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
        SpringApplication.run(CloudAlibabaConsumer84Application.class, args);
    }
}

    业务类

package com.zjt.cloud.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;

/**
 * 配置类 使用 RestTemplate 作为远程调用
 * @author zjt
 * @date 2020-10-22
 */
@Configuration
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() { 
        return new RestTemplate();
    }

}
package com.zjt.cloud.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.zjt.cloud.domain.CommonResult;
import com.zjt.cloud.domain.Payment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * @author zjt
 * @date 2020-10-22
 * 使用 RestTemplate 测试 fallback 与 blockHandler 
 */
@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    public static final String RPC_URL = "http://nacos-payment-provider";

    @Autowired
    private RestTemplate template;

    // 无任何配置
    @GetMapping("/payment/{id}")
    @SentinelResource(value = "consumer-payment")
    public CommonResult<Payment> findPayment(@PathVariable("id") Long id) {
        return this.getPayment(id);
    }

    @GetMapping("/only-fallback/{id}")
    @SentinelResource(value = "only-fallback", fallback = "paymentFallback") //fallback 仅处理业务异常
    public CommonResult<Payment> findPaymentOnlyFallback(@PathVariable("id") Long id) {
        return this.getPayment(id);
    }

    public CommonResult<Payment> paymentFallback(Long id, Throwable e) {
        Payment payment = new Payment(id, null);
        return new CommonResult<>(500, "业务异常->进入备用方法->异常内容:" + e.getMessage(), payment);
    }

    @GetMapping("/only-block/{id}")
    @SentinelResource(value = "only-block", blockHandler = "paymentBlock") // blockHandler仅处理配置异常
    public CommonResult<Payment> findPaymentOnlyBlock(@PathVariable("id") Long id) {
        return this.getPayment(id);
    }

    public CommonResult<Payment> paymentBlock(Long id, BlockException e) {
        Payment payment = new Payment(id, null);
        return new CommonResult<>(500, "sentinel 流控异常->进入备用方法->异常内容:" + e.getMessage(), payment);
    }


    // 配置处理配置异常 与 仅处理业务异常 配置异常的优先级高于 业务异常
    // 忽略异常 IllegalArgumentException
    @GetMapping("/payment-a/{id}")
    @SentinelResource(value = "consumer-payment-a", blockHandler = "paymentBlock", fallback = "paymentFallback",
            exceptionsToIgnore = {IllegalArgumentException.class})
    public CommonResult<Payment> findPaymentA(@PathVariable("id") Long id) {
        return this.getPayment(id);
    }

    private CommonResult<Payment> getPayment(Long id) {
        CommonResult<Payment> result = template.getForObject(RPC_URL + "/payment/" + id, CommonResult.class, id);
        if (id == 4) {
            throw new IllegalArgumentException("非法参数");
        } else if (result != null && result.getResult() == null) {
            throw new NullPointerException("空指针异常,未找到对应记录");
        }
        return result;
    }
}
package com.zjt.cloud.rpc;

import com.zjt.cloud.domain.CommonResult;
import com.zjt.cloud.domain.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * @author zjt
 * @date 2020-10-22
 * 使用 Feign 实现远程调用与服务降级
 */
@Component
@FeignClient(value = "nacos-payment-provider", fallback = PaymentRPCFallbackServer.class)
public interface PaymentRPCServer {

    @GetMapping("/payment/{id}")
    CommonResult<Payment> getPayment(@PathVariable(value = "id") Long id);

}

package com.zjt.cloud.rpc;

import com.zjt.cloud.domain.CommonResult;
import com.zjt.cloud.domain.Payment;
import org.springframework.stereotype.Component;

/**
 * @author zjt
 * @date 2020-10-22
 * feign 的降级方法
 */
@Component
public class PaymentRPCFallbackServer implements PaymentRPCServer{

    @Override
    public CommonResult<Payment> getPayment(Long id) {
        return new CommonResult<>(4444, "服务降级", new Payment(id, "errorSerial"));
    }
}

package com.alibaba.cloud.sentinel.feign;

import feign.Contract;
import feign.MethodMetadata;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author zjt
 * @date 2020-10-22
 * 由于我的版本问题 sentinel与fegin不兼容
 * Hoxton.SR1 中,fegin.context接口方法的定义为parseAndValidatateMetadata
 * Hoxton.SR3 中,fegin.context接口方法的定义为parseAndValidateMetadata
 * 在自己工程合适的地方,新建package:com.alibaba.cloud.sentinel.feign
 */
public class SentinelContractHolder implements Contract {

    /**
     * map key is constructed by ClassFullName + configKey. configKey is constructed by
     * {@link feign.Feign#configKey}
     */
    public final static Map<String, MethodMetadata> METADATA_MAP = new HashMap<>();
    private final Contract delegate;

    public SentinelContractHolder(Contract delegate) {
        this.delegate = delegate;
    }

    @Override
    public List<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) {
        List<MethodMetadata> metadatas = delegate.parseAndValidatateMetadata(targetType);
        metadatas.forEach(metadata -> METADATA_MAP
                .put(targetType.getName() + metadata.configKey(), metadata));
        return metadatas;
    }
}

    测试 普通降级
在这里插入图片描述
    feign降级
在这里插入图片描述

规则持久化

    存在问题,注册进入sentinel的微服务模块,每次重启配置的规则就会消失,不友好,需要一定的持久化方案。
    将限流的规则持久化进入Nacos保存,只要刷新8401 的某个rest地址,sentinel控制台的限流规则将会可以看到。Nacos里面的配置规格不删除,sentinel的流控规则持续有效。

一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:
    resource:资源名,即限流规则的作用对象
    count: 限流阈值
    grade: 限流阈值类型(QPS 或并发线程数)
    limitApp: 流控针对的调用来源,若为 default 则不区分调用来源
    strategy: 调用关系限流策略
    controlBehavior: 流量控制效果(直接拒绝、Warm Up、匀速排队)】
在这里插入图片描述

    8401修改
    pom.xml 引入

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

    application.yml

server:
  port: 8401

spring:
  application:
    name: cloud-sentinel-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 #Nacos服务注册中心地址
    sentinel:
      transport:
        dashboard: localhost:10010 #配置Sentinel dashboard地址
        port: 8719 #本地启动 HTTP API Server 的端口号 若端口冲突会自动向下探测可用的端口
                   # 指定应用与sentinel控制台交互的端口,本地应用会起一个占用该端口的 Http Server
                   # 这里的 spring.cloud.sentinel.transport.port 端口配置会在应用对应的机器上启动一个 Http Server,
                   # 该 Server 会与 Sentinel 控制台做交互。比如 Sentinel 控制台添加了一个限流规则,
                   # 会把规则数据 push 给这个 Http Server 接收,Http Server 再将规则注册到 Sentinel 中
      datasource:
        ds1:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow # 规则类型

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

    
    Nacos 新增配置
在这里插入图片描述
    测试 流控规则被写入sentinel
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值