92-Spring Cloud [Alibaba] 2021.0.4 JDK17之前的最新生产版(第7章:断路器:服务的降级和熔断(组件3))

第7章:🚩🚩断路器:服务的降级和熔断(组件3)

断路器的作用:

起到保护微服务的作用:
  1. 如果微服务出了故障,比如响应很慢或产生了异常,那么在别的微服务调用的时候,可能会导致别的服务也跟着出现故障,导致整个微服务都不可用。
  2. 限制请求数量和线程数,保护服务器在合理的压力承受范围
微服务
断路器也至少有三种:
断路器早期的时候Spring Cloud是 hystrix(停止更新了),后面可以使用阿里巴巴的 sentinel ,还有最新版的 circuitbreaker
服务熔断是只服务的某个接口,在压力太大(比如并发太多、请求太多、频繁抛异常等)情况下,短暂的不能对外提供服务,可以在一定时间内尝试恢复到正常状态的情况。( 临时哑火,可以自动恢复
服务降级:是指整个项目在压力大的情况下, 主动关闭某些边缘的、不重要的服务(全部相关接口都不能访问),让核心服务享有CPU和内存进行对外提供服务。( 弃车保帅,需手动恢复
服务回退:服务回退是指服务提供方不能提供服务的时候,服务调用方做一个备选接口,这样不至于报错

1 使用circuitbreaker:

1 🚩调用方做服务回退:
pom文件加依赖:
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
OpenFeign的假接口:
package com.lzket.feign;

import com.lzket.feign.fallback.OrderFeignFallBack;
import com.lzket.feign.fallback.factory.LzketFallBackFactory;
import com.lzket.pojo.dto.ResponseDTO;
import com.lzket.pojo.entity.User;
import com.lzket.pojo.query.UserQuery;
import com.lzket.pojo.vo.UserVO;
import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author:菩提老师
 * @学习地址:lzket.com *
 * name:表示微服务的名称,注意,nacos的话需要服务名大小写区分
 * path:表示该接口的方法加上统一请求前缀
 * 本章知识点在实际开发中很重要,请务必掌握。
 */
@FeignClient(contextId = "orderClient1",name = "order-nacos-loadbalancer", path = "/order",fallbackFactory = LzketFallBackFactory.class)
public interface OrderFeign {
    // 传递单个参数
    @RequestMapping("/findOrderById")
    String findOrderById(String orderId);

    // 传递单个参数 @PathVariable 底层还是GET请求
    @RequestMapping("/findOrder/{orderId}")
    String findOrder(@PathVariable Long orderId);

}
服务回退可以使用工厂类,也可以使用特定的一个类,都需要是一个Spring的Bean。且都需要实现 假接口
工厂类:
package com.lzket.feign.fallback.factory;

import com.lzket.feign.OrderFeign;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;

/**
 * @author:菩提老师
 * @学习地址:lzket.com
 */
@Component
public class LzketFallBackFactory implements FallbackFactory<OrderFeign> {
    @Override
    public OrderFeign create(Throwable cause) {
        return new OrderFeign() {
            @Override
            public String findOrderById(String orderId) {
                return "调用失败";
            }

            @Override
            public String findOrder(Long orderId) {
              return   "调用失败";
            }
        };
    }
}
不用工厂类:
package com.lzket.feign.fallback;

import com.lzket.feign.OrderFeign;
import org.springframework.stereotype.Component;

/**
 * @author:菩提老师
 * @学习地址:lzket.com
 */
@Component
public class OrderFeignFallBack implements OrderFeign {
    @Override
    public String findOrderById(String orderId) {
        return "订单服务不可用,请稍后尝试";
    }

    @Override
    public String findOrder(Long orderId) {
        return "订单服务不可用,请稍后尝试...";
    }

}
yml文件开启断路器:
🚩注意事项:
1、需要添加断路器的依赖,可以是spring-cloud-starter-circuitbreaker-resilience4j、Hystrix、Sentinel
2、假接口需要指定fallback或者fallbackFactory
3、回退的自己写的假接口的实现类,必须是Spring的 Bean,也就是你可以加上@Component注解
2 🚩提供方做熔断功能
主启动:
package com.lzket;

import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreaker;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.concurrent.ThreadLocalRandom;


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

    @Resource
    Resilience4JCircuitBreakerFactory f;

    @RequestMapping("/findOrderById")
    String findOrderById(Integer id) {
        Resilience4JCircuitBreaker resilience4JCircuitBreaker = f.create("findOrderById:熔断");
        return resilience4JCircuitBreaker.run(() -> {
            // 这里边应该是去调用Service的接口
            try {
//                Thread.sleep(1500);// 超时熔断
                if (id == 1) {
                    System.out.println(1 / 0);// 报错熔断
                } else {
                    System.out.println("正常返回");
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return "我是订单服务:正常提供服务:" + ThreadLocalRandom.current().nextInt(100);
        }, e -> {
            return "熔断啦...." + e.getMessage();
        });

    }


}

@Configuration
class LzketCircuitBreakerConfig {
    //  默认的配置 一秒超时熔断
    @Bean
    public Customizer<Resilience4JCircuitBreakerFactory> defaultCustomizer() {

// 1. 所有Exception以及其子类都认为是失败.
// 2. 滑动窗口采用基于计时的,并且记录最近10秒的请求.
// 3. 触发断路器判断必须在10秒内至少有5个请求,在失败比例达到30%以上之后,断路器变为:OPEN.
// 4. 断路器OPEN之后,在2秒后自动转化为HALF_OPEN.
// 5. 断路器在HALF_OPEN之后,允许通过的请求数量为:3个
        CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
                // 滑动窗口类型(TIME_BASED:时间 / COUNT_BASED:计数器 )
                .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED)
                // 滑动窗口大小(记录最近10秒的请求)
                .slidingWindowSize(10)
                // 最小请求个数.只有在滑动窗口内,请求个数达到这个个数,才会触发CircuitBreaker对于是否打开断路器的判断
                .minimumNumberOfCalls(5)
                // 当CircuitBreaker处于HALF_OPEN状态的时候,允许通过的请求数量
                .permittedNumberOfCallsInHalfOpenState(3)
                // 自动从OPEN状态变成HALF_OPEN,即使没有请求过来.
                .automaticTransitionFromOpenToHalfOpenEnabled(true)
                // 从OPEN状态变成HALF_OPEN状态需要的等待2秒
                .waitDurationInOpenState(Duration.ofSeconds(5))
                // 失败率达到30%,CircuitBreaker就会变成OPEN状态
                .failureRateThreshold(30)
                // 所有Exception异常会统计为失败.
                .recordExceptions(Exception.class)
                //
                .build();

        return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
//                .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults())
                .circuitBreakerConfig(circuitBreakerConfig)
                .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(5)).build())
                .build());
    }
}
🚩断路器的状态:
  1. 闭合(Closed):默认情况下Circuit Breaker是关闭的,此时允许操作执行。Circuit Breaker内部记录着最近失败的次数,如果对应的操作执行失败,次数就会续一次。如果在某个时间段内,失败次数(或者失败比率)达到阈值,Circuit Breaker会转换到开启(Open)状态。在开启状态中,Circuit Breaker会启用一个超时计时器,设这个计时器的目的是给集群相应的时间来恢复故障。当计时器时间到的时候,Circuit Breaker会转换到半开启(Half-Open)状态。
  2. 开启(Open):在此状态下,执行对应的操作将会立即失败并且立即抛出异常。
  3. 半开启(Half-Open):在此状态下,Circuit Breaker 会允许执行一定数量的操作。如果所有操作全部成功,Circuit Breaker就会假定故障已经恢复,它就会转换到关闭状态,并且重置失败次数。如果其中 任意一次 操作失败了,Circuit Breaker就会认为故障仍然存在,所以它会转换到开启状态并再次开启计时器(再给系统一些时间使其从失败中恢复)。
当业务层抛异常或者超时,都会触发熔断。
  1. CLOSED状态时,请求正常放行
  2. 请求失败率达到设定阈值时,变为OPEN状态,此时请求全部不放行
  3. OPEN状态持续设定时间后,进入半开状态(HALE_OPEN),放过部分请求
  4. 半开状态下,失败率低于设定阈值,就进入CLOSE状态,即全部放行
  5. 半开状态下,失败率高于设定阈值,就进入OPEN状态,即全部不放行
CircuitBreakerConfig配置
    //请求调用失败的阈值,百分比。默认是50%,即服务A调用服务B,此时B调用失败即算作一个失败调用
    public static final int DEFAULT_FAILURE_RATE_THRESHOLD = 50; // Percentage
    //慢调用的阈值,百分比
    public static final int DEFAULT_SLOW_CALL_RATE_THRESHOLD = 100; // Percentage
    // 熔断器在打开状态时的持续时间。默认是60秒
    public static final int DEFAULT_WAIT_DURATION_IN_OPEN_STATE = 60; // Seconds
    // 熔断器在半开状态下的ring buffer大小。默认10,不超过此值就可以通过请求
    public static final int DEFAULT_PERMITTED_CALLS_IN_HALF_OPEN_STATE = 10;
    // 熔断器在关闭状态下的可以计算失败率的最小值,默认100
    public static final int DEFAULT_MINIMUM_NUMBER_OF_CALLS = 100;
    //滑动窗口大小,熔断器在关闭状态下的ring buffer大小
    public static final int DEFAULT_SLIDING_WINDOW_SIZE = 100;
    //慢调用的时间,即当服务A调用服务B时,B的执行时间超过了60秒就算作是慢调用
    public static final int DEFAULT_SLOW_CALL_DURATION_THRESHOLD = 60; // Seconds
    //滑动窗口类型,默认为基于计数的 COUNT_BASED
    public static final SlidingWindowType DEFAULT_SLIDING_WINDOW_TYPE = SlidingWindowType.COUNT_BASED;
    public static final boolean DEFAULT_WRITABLE_STACK_TRACE_ENABLED = true;
    // 是否记录请求调用失败的断言,默认所有异常都记录
    private static final Predicate<Throwable> DEFAULT_RECORD_EXCEPTION_PREDICATE = throwable -> true;
    //忽略异常
    private static final Predicate<Throwable> DEFAULT_IGNORE_EXCEPTION_PREDICATE = throwable -> false;
    // The default exception predicate counts all exceptions as failures.
    private Predicate<Throwable> recordExceptionPredicate = DEFAULT_RECORD_EXCEPTION_PREDICATE;
    // The default exception predicate ignores no exceptions.
    private Predicate<Throwable> ignoreExceptionPredicate = DEFAULT_IGNORE_EXCEPTION_PREDICATE;

    // 默认为false,是否自动从打开到半开,当waitDurationInOpenState时间一过,是否自动从OPEN切换到HALF_OPEN
    // true:waitDurationInOpenState到期后open自动变为half_open
    //false,得等到再有请求后状态才会变为half_open,否则即使waitDurationInOpenState到期状态依然是open    
    private boolean automaticTransitionFromOpenToHalfOpenEnabled = false;

2 使用 hystrix实现断路器【老版本】(了解)

断路器还有早期的Hystrix,有阿里巴巴的sentinel
Hystrix案例:
package boss.zkt.hystrix;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * circuitBreaker.requestVolumeThreshold(默认:20 个请求)
 * 并且失败百分比大于circuitBreaker.errorThresholdPercentage(默认:>50%)
 * 在定义的滚动窗口metrics.rollingStats.timeInMilliseconds(默认:10 秒)内,电路打开并且不进行调用
 * 开发人员可以提供回退。
 */
@SpringBootApplication
@EnableCircuitBreaker// 开启断路器
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@RestController
class StoreIntegration {

    int x=0;
    @HystrixCommand(fallbackMethod = "defaultStores")
    @RequestMapping("/test")
    public Object getStores(HttpServletRequest request) {
        //do stuff that might fail
        System.out.println("进来咯"+(x++));
        System.out.println(1 / 0);
        throw new NullPointerException();
//        return "喜喜";
    }

    // 这里的方法必须一样
    public Object defaultStores(HttpServletRequest request) {
        return "Hystrix断路咯!"+x;
    }
}
可以配置线程池属性:
  @HystrixCommand(commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500")
        },
                threadPoolProperties = {
                        @HystrixProperty(name = "coreSize", value = "30"),
                        @HystrixProperty(name = "maxQueueSize", value = "101"),
                        @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                        @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"),
                        @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),
                        @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1440")
        })
    public User getUserById(String id) {
        return userResource.getUserById(id);
    }
1、pom文件:
<?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>
    <groupId>boss.zkt</groupId>
    <artifactId>15-circuit-breaker-with-hystrix</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>15-circuit-breaker-with-hystrix</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
        <spring-cloud.version>Hoxton.SR9</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>


    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>boss.zkt.hystrix.Application</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
主启动类:
package boss.zkt.hystrix;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * circuitBreaker.requestVolumeThreshold(默认:20 个请求)
 * 并且失败百分比大于circuitBreaker.errorThresholdPercentage(默认:>50%)
 * 在定义的滚动窗口metrics.rollingStats.timeInMilliseconds(默认:10 秒)内,电路打开并且不进行调用
 * 开发人员可以提供回退。
 * HystrixCommandProperties 可以配置 命令属性
 * HystrixCommandProperties 可 以配置 线程池属性
 */
@SpringBootApplication
@EnableCircuitBreaker// 开启断路器
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@RestController
class StoreIntegration {

    int x = 0;
//    @HystrixCommand(commandProperties = {
//            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500")
//    },
//            threadPoolProperties = {
//                    @HystrixProperty(name = "coreSize", value = "30"),
//                    @HystrixProperty(name = "maxQueueSize", value = "101"),
//                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
//                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"),
//                    @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),
//                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1440")
//            })

    @HystrixCommand(fallbackMethod = "defaultStores")
    @RequestMapping("/test")
    public Object getStores(HttpServletRequest request) {
        //do stuff that might fail
        System.out.println("进来咯" + (x++));
        System.out.println(1 / 0);
        throw new NullPointerException();
//        return "喜喜";
    }

    // 这里的方法必须一样
    public Object defaultStores(HttpServletRequest request) {
        return "Hystrix断路咯!" + x;
    }
}

3 🚩Spring Cloud Alibaba Sentinel

1 Sentinel简介【阿里巴巴的,功能厉害】
以“流量”为切入点,在流量控制、断路、负载保护等多个领域开展业务,保障业务可靠性。
Sentinel具有以下特点:
  • 丰富场景:Sentinel 支持阿里巴巴双十一重点场景,如秒杀(即控制突发流量,使其在系统容量可接受范围内)、消息负载转移、不可靠的下游应用的断路。
  • 全面的实时监控: Sentinel 提供实时监控能力。您可以看到您的服务器的监控数据,精确到秒级,甚至可以看到少于 500 个节点的集群的整体运行状态。
  • 广泛的开源生态系统: Sentinel 提供开箱即用的模块,可以轻松与其他开源框架/库集成,例如 Spring Cloud、Dubbo 和 gRPC。使用 Sentinel,只需要引入相关依赖并做一些简单的配置即可。
  • Sound SPI Extensions: Sentinel 提供简单易用的声音 SPI 扩展接口。您可以使用 SPI 扩展快速自定义逻辑,例如,您可以定义自己的规则管理,或适应特定的数据源。
2 怎么用
1、引入pom文件:
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2、主启动:
package com.lzket;

import com.alibaba.cloud.circuitbreaker.sentinel.ReactiveSentinelCircuitBreakerFactory;
import com.alibaba.cloud.circuitbreaker.sentinel.SentinelCircuitBreakerFactory;
import com.alibaba.cloud.circuitbreaker.sentinel.SentinelConfigBuilder;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;
import java.util.List;


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

}

@RestController
class TestController {
    @GetMapping(value = "/hello")
    @SentinelResource(value = "hello", fallback = "aaa", blockHandler = "bbb")
    public String hello(Integer id) {
        if (id == 1) {
            System.out.println(1 / 0);
        }
        return "Hello Sentinel";
    }

    public String aaa(Integer id, Throwable e) {
        System.out.println("我是异常哦aaa");
        return "异常aaa";
    }

    public String bbb(Integer id, BlockException e) {
        System.out.println("我是异常哦bbb");
        return "异常bbb";
    }
}

@Configuration
class LzketSentinelConfig {


    @Bean
    public Customizer<ReactiveSentinelCircuitBreakerFactory> defaultCustomizer() {
        return factory -> factory.configureDefault(id -> new SentinelConfigBuilder(id)
                .build());
    }

    // 给某个 指定的断路器创建规则
//    @Bean
//    public Customizer<SentinelCircuitBreakerFactory> defaultCustomizer() {
//        String slowId = "slow";
//        List<DegradeRule> rules = Collections.singletonList(
//                new DegradeRule(slowId).setGrade(RuleConstant.DEGRADE_GRADE_RT)
//                        .setCount(100)
//                        .setTimeWindow(10)
//        );
//        return factory -> factory.configure(builder -> builder.rules(rules), slowId);
//    }
}
3、yml:
server:
  port: 8032 #
spring:
  application:
    name: order-nacos-loadbalancer #
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.124.10:8848 #配置Nacos地址
        username: nacos # 默认的用户名和密码,可以到nacos控制台进行更改
        password: nacos # 默认的用户名和密码,可以到nacos控制台进行更改
    loadbalancer:
      nacos:
        enabled: true # 开启nacos的负载均衡随机权重算法
    sentinel:
      eager: true # 取消Sentinel控制台懒加载功能
      transport:
        port: 8719
        dashboard: 192.168.124.10:8858
feign:
  client:
    refresh-enabled: true #
    config:
      default:
        connectTimeout: 15000 # 连接超时
        readTimeout: 30000   # 请求超时
        loggerLevel: basic  # 记录请求方法和 URL 以及响应状态代码和执行时间
  httpclient:
    enabled: true # Feign底层默认使用 httpclient 来请求 另外还有OKhttp
    hc5:
      enabled: false # 禁用【可选】
  okhttp:
    enabled: false # 禁用 【可选】
#  hystrix:
#    enabled: true #服务 回退
#  circuitbreaker:
#    enabled: true # 上面无效 使用下面 服务降级
  sentinel:
    enabled: true # 上面无效 使用下面 服务降级
3 @SentinelResource 注解
注意:注解方式埋点不支持 private 方法。
@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:
  • value:资源名称,必需项(不能为空)
  • entryType:entry 类型,可选项(默认为 EntryType.OUT)
  • blockHandler / blockHandlerClass: blockHandler 对应处理 BlockException 的函数名称,可选项。blockHandler 函数访问范围需要是 public,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException。blockHandler 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 blockHandlerClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • fallback / fallbackClass:fallback 函数名称,可选项,用于在抛出异常的时候提供 fallback 处理逻辑。fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。fallback 函数签名和位置要求:
    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要和原函数一致,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • fallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • defaultFallback(since 1.6.0):默认的 fallback 函数名称,可选项,通常用于通用的 fallback 逻辑(即可以用于很多服务或方法)。默认 fallback 函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback 函数签名要求:
    • 返回值类型必须与原函数返回值类型一致;
    • 方法参数列表需要为空,或者可以额外多一个 Throwable 类型的参数用于接收对应的异常。
    • defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定 fallbackClass 为对应的类的 Class 对象,注意对应的函数必需为 static 函数,否则无法解析。
  • exceptionsToIgnore(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入 fallback 逻辑中,而是会原样抛出。
1.8.0 版本开始,defaultFallback 支持在类级别进行配置。
注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理, 不能针对业务异常进行处理
特别地, 若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。若未配置 blockHandler、fallback 和 defaultFallback,则被限流降级时会将 BlockException 直接抛出(若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException)。
4 sentinel控制台
docker启动:
#!/bin/bash
docker rm -f sentinel  
docker run --name sentinel  -d --net=host -d  bladex/sentinel-dashboard
访问路径:http://192.168.124.10:8858
传统方式启动:
启动脚本:start-sentinel.bat:
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.4.jar

接下来就可以按照一堆的规则,去配置了。
5 🚩配置流控:
流控规则解释:
资源名: 请求路径或资源名称。(注意:如果是写的路径,那么会调用sentinel自身的异常备选方法,只有配置自己写的资源名称,才会触发自己写的备选方法)
针对来源:是指调用方的微服务名称,不写就表示default,不限制
阈值类型:QPS,每秒请求次数 。线程数:是指当前接口有多少个线程同时调用。阈值:达到条件就触发限流
是否集群:默认不需要
流控模式:
  1. 直接:达到限流条件,直接触发
  2. 关联:当关联的资源达到限流条件,触发自己限流,所以需要填关联资源
  3. 链路:只记录指定入口链路上的流量
流控效果:
  1. 快速失败:直接失败,抛出异常==>>blockHandler
  2. Warm Up(预热):根据codeFactor(冷加载因子,默认3)的值,经过预热时常,阈值才达到预设的值(也就是刚开始是3,慢慢的变成设置的值,需要一定的时间)
  3. 排队等待,当阈值类型是QPS的时候,让请求匀速排队的请求服务。
配置示例:
注意看区别:
6 🚩降级规则:sentinel的降级就是熔断
  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 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断
配置示例:
代码:
  @GetMapping(value = "/hello2")
    @SentinelResource(value = "hello2", blockHandler = "bbb")
    public String hello2(Integer id) {
        if (id == 1) {
//            System.out.println(1 / 0);
            try {
                Thread.sleep(1100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        return "Hello Sentinel";
    }
    public String bbb(Integer id, BlockException e) {
        System.out.println("我是异常哦bbb");
        return "异常bbb";
    }
7 🚩热点参数限流:
第一个参数值如果是1,且如果1秒内达到1次,就限流:
@GetMapping(value = "/hello3")
@SentinelResource(value = "hello3", blockHandler = "bbb")
public String hello3(Integer id) {

    return "Hello Sentinel";
}
public String bbb(Integer id, BlockException e) {
    System.out.println("我是异常哦bbb");
    return "异常bbb";
}
8 系统规则,对当前微服务而言
系统规则支持以下的模式:
  1. Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。
  2. CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
  3. 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  4. 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  5. 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
9 🚩sentinel持久化
当重启sentinel,重启服务之后,配置的规则就消失了, 可以把配置规则进行持久化到nacos。nacos可以作为配置中心。
1 pom文件加依赖
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2 yml文件
server:
  port: 8032 #
spring:
  application:
    name: order-nacos-loadbalancer #
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.124.10:8848 #配置Nacos地址
        username: nacos # 默认的用户名和密码,可以到nacos控制台进行更改
        password: nacos # 默认的用户名和密码,可以到nacos控制台进行更改
    loadbalancer:
      nacos:
        enabled: true # 开启nacos的负载均衡随机权重算法
    sentinel:
      eager: true # 取消Sentinel控制台懒加载功能
      transport:
        port: 8719
        dashboard: 192.168.124.10:8858
      datasource:
        flow: # 流控规则模块
          nacos:
            server-addr: 192.168.124.10:8848
            dataId: ${spring.application.name} # 数据配置id,需要在nacos的配置中心里边配置
            groupId: DEFAULT_GROUP
            data-type: json
            rule-type: flow  # 流控模块
            username: nacos # 账户
            password: nacos # 密码

feign:
  client:
    refresh-enabled: true #
    config:
      default:
        connectTimeout: 15000 # 连接超时
        readTimeout: 30000   # 请求超时
        loggerLevel: basic  # 记录请求方法和 URL 以及响应状态代码和执行时间
  httpclient:
    enabled: true # Feign底层默认使用 httpclient 来请求 另外还有OKhttp
    hc5:
      enabled: false # 禁用【可选】
  okhttp:
    enabled: false # 禁用 【可选】
#  hystrix:
#    enabled: true #服务 回退
#  circuitbreaker:
#    enabled: true # 上面无效 使用下面 服务降级
  sentinel:
    enabled: true # 上面无效 使用下面 服务降级
3 nacos配置中心增加配置:
[
    {
        "resource": "hello",
        "limitApp": "default",
        "grade": 1,
        "count": 1,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]

配置解释:

resource:资源名称;
limitApp:来源应用;
grade:阈值类型,0表示线程数,1表示QPS;
count:单机阈值;
strategy:流控模式,0表示直接,1表示关联,2表示链路;
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
clusterMode:是否集群。
4 验证:重启 服务,规则仍然生效

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菩提老师

如果帮到了您,感谢您的鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值