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

断路器的作用:
起到保护微服务的作用:
- 如果微服务出了故障,比如响应很慢或产生了异常,那么在别的微服务调用的时候,可能会导致别的服务也跟着出现故障,导致整个微服务都不可用。
- 限制请求数量和线程数,保护服务器在合理的压力承受范围
微服务
断路器也至少有三种:
服务熔断:
是只服务的某个接口,在压力太大(比如并发太多、请求太多、频繁抛异常等)情况下,短暂的不能对外提供服务,可以在一定时间内尝试恢复到正常状态的情况。(
临时哑火,可以自动恢复
)
服务降级:是指整个项目在压力大的情况下,
主动关闭某些边缘的、不重要的服务(全部相关接口都不能访问),让核心服务享有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、Sentinel2、假接口需要指定fallback或者fallbackFactory3、回退的自己写的假接口的实现类,必须是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());
}
}
🚩断路器的状态:
- 闭合(Closed):默认情况下Circuit Breaker是关闭的,此时允许操作执行。Circuit Breaker内部记录着最近失败的次数,如果对应的操作执行失败,次数就会续一次。如果在某个时间段内,失败次数(或者失败比率)达到阈值,Circuit Breaker会转换到开启(Open)状态。在开启状态中,Circuit Breaker会启用一个超时计时器,设这个计时器的目的是给集群相应的时间来恢复故障。当计时器时间到的时候,Circuit Breaker会转换到半开启(Half-Open)状态。
- 开启(Open):在此状态下,执行对应的操作将会立即失败并且立即抛出异常。
- 半开启(Half-Open):在此状态下,Circuit Breaker 会允许执行一定数量的操作。如果所有操作全部成功,Circuit Breaker就会假定故障已经恢复,它就会转换到关闭状态,并且重置失败次数。如果其中 任意一次 操作失败了,Circuit Breaker就会认为故障仍然存在,所以它会转换到开启状态并再次开启计时器(再给系统一些时间使其从失败中恢复)。
当业务层抛异常或者超时,都会触发熔断。
- CLOSED状态时,请求正常放行
- 请求失败率达到设定阈值时,变为OPEN状态,此时请求全部不放行
- OPEN状态持续设定时间后,进入半开状态(HALE_OPEN),放过部分请求
- 半开状态下,失败率低于设定阈值,就进入CLOSE状态,即全部放行
- 半开状态下,失败率高于设定阈值,就进入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,每秒请求次数 。线程数:是指当前接口有多少个线程同时调用。阈值:达到条件就触发限流
是否集群:默认不需要
流控模式:
- 直接:达到限流条件,直接触发
- 关联:当关联的资源达到限流条件,触发自己限流,所以需要填关联资源
- 链路:只记录指定入口链路上的流量
流控效果:
- 快速失败:直接失败,抛出异常==>>blockHandler
- Warm Up(预热):根据codeFactor(冷加载因子,默认3)的值,经过预热时常,阈值才达到预设的值(也就是刚开始是3,慢慢的变成设置的值,需要一定的时间)
- 排队等待,当阈值类型是QPS的时候,让请求匀速排队的请求服务。
配置示例:

注意看区别:

6 🚩降级规则:sentinel的降级就是熔断


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

系统规则支持以下的模式:
- 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 达到阈值即触发系统保护。
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:是否集群。