Hystrix断路器
Spring Cloud 2020.0.0版本开始,已移除大部分相关的Netflix OSS组件,Eureka没有被移除,feign使用openfeign封装
- Spring Cloud Netflix Zuul - Zuul是一个API网关服务,Spring Cloud推荐使用Spring Cloud Gateway作为替代。
- Spring Cloud Netflix Ribbon - Ribbon是一个客户端负载均衡器,Spring Cloud推荐使用Spring Cloud LoadBalancer作为替代。
- Spring Cloud Netflix Hystrix - Hystrix是一个熔断器和延迟容忍库,Spring Cloud推荐使用Resilience4j或Sentinel作为替代。
- Spring Cloud Netflix Archaius - Archaius是一个配置管理库,Spring Cloud推荐使用Spring Cloud Config作为替代。
雪崩效应
多个微服务链式调用,如果调用链路上某个微服务不可用或者响应的时间过长,那么消费者和调用链路上游将会处于阻塞状态,如果此时有大量请求访问,线程资源很容易被消耗完毕,导致整个系统的瘫痪
HystrixCommand配置
public @interface HystrixCommand {
// 定义 Hystrix 命令的分组名称,便于统计和监控。通过组名,可以对同一组中的所有命令进行统一的配置和监控。
// 创建线程池的默认以groupKey命名,如果未指定,则groupKey默认为类名
String groupKey() default "";
// 定义 Hystrix 命令的名称,通过命令名,可以对单个命令进行精细化的配置和监控
// 如果未指定,则注解标识的方法名默认为命令名
String commandKey() default "";
// 定义 Hystrix 命令的线程池名称,相同名称的方法会使用同一个线程池
//如果未指定,使用groupKey作为线程池名。
String threadPoolKey() default "";
// 定义服务降级时调用的方法名称
String fallbackMethod() default "";
// 定义 Hystrix 命令的一些属性,比如超时时间、熔断器的阈值等
HystrixProperty[] commandProperties() default {};
// 定义 Hystrix 命令的线程池属性,比如线程池的大小、队列的大小等
HystrixProperty[] threadPoolProperties() default {};
// 定义哪些异常类型会被 Hystrix 忽略,对于这些异常,Hystrix 不会触发服务熔断和降级
Class<? extends Throwable>[] ignoreExceptions() default {};
// 定义 Hystrix 命令的执行模式,可以是 EAGER 或 LAZY
ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;
// 定义哪些异常类型会被 Hystrix 视为失败,对于这些异常,Hystrix 会触发服务熔断和降级
HystrixException[] raiseHystrixExceptions() default {};
// 定义一个全局的降级方法,当服务调用失败或者被熔断,且没有定义 fallbackMethod() 时,Hystrix 会调用这个方法来返回一个默认的结果
String defaultFallback() default "";
}Hystrix概述

服务降级Fallback:服务不可用或响应时间过长,就返回一个备选响应,避免调用方和调用链路上游阻塞,使得故障在分布式系统中的蔓延
- 程序运行异常
- 超时自动降级(从命令开始执行时就开始计时,包括所有的重试操作。)
- 服务熔断触发服务降级
- 线程池/信号量打满也会导致服务降级
- 人工降级
服务熔断Breaker
- 类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
服务限流Flowlimit
- 秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行
xml<!--hystrix依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
服务降级
- 设置消费者自身调用超时时间的峰值,峰值内可以正常运行,超过了需要有兜底的方法处理,作服务降级fallback
- 注意:服务降级可以在服务提供者侧,也可以在服务消费者侧。更多是在服务消费者侧。因为存在客户端去调用服务端,碰上服务端宕机或关闭。所以服务降级处理在客户端实现,与服务端无关,可实现解耦。
在提供者端
//service层
@Service
public class PaymentServiceImpl implements PaymentService {
//超时降级演示
@HystrixCommand(fallbackMethod = "payment_TimeoutHandler",commandProperties = {
//注意openFeign也有设置时间,如果超过其设置时间也会走兜底方法
//5秒钟以内就是正常的业务逻辑
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="5000")
})
@Override
public String payment_Timeout(Integer id) {
//int timeNumber = 3; //小于等于3秒算是正常情况
int timeNumber = 15; //模拟非正常情况
//int i = 1/0 ; //模拟非正常情况
try {
TimeUnit.SECONDS.sleep(timeNumber);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:"+Thread.currentThread().getName()+" payment_Timeout,id="+id+" \t o(╥﹏╥)o 耗时:"+timeNumber;
}
//兜底方法,上面方法出问题,我来处理,返回一个出错信息
public String payment_TimeoutHandler(Integer id) {
return "线程池:"+Thread.currentThread().getName()+" payment_TimeoutHandler,系统繁忙,请稍后再试\t o(╥﹏╥)o ";
}
}
//主启动类添加@EnableHystrix,作用同@EnableCircuitBreaker,@EnableHystrix继承了@EnableCircuitBreaker
@SpringBootApplication
@EnableHystrix
@EnableEurekaClient
public class HystrixMain {
public static void main(String[] args) {
SpringApplication.run(HystrixMain.class, args);
}
}在消费者端
配置文件
# 在Feign中启用Hystrix功能,以增加对远程服务调用的熔断和容错能力
# 在SpringCloud2020.0.0后版本,openfeign已经移除了Hystrix,以下配置无效,后续代码也无效
feign:
hystrix:
enabled: true 代码配置
//主启动类
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix
public class HystrixConsumerMain {
public static void main(String[] args) {
SpringApplication.run(HystrixConsumerMain.class, args);
}
}
@RestController
@Slf4j
public class HystrixController {
@Resource
private PaymenService paymentService;
//指定兜底方法,和超时时间为1.5秒
@HystrixCommand(fallbackMethod = "HandleException",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
})
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
String result = paymentService.paymentInfo_TimeOut( id);
log.info("*******result:"+result);
return result;
}
//兜底方法的返回值和参数要和原方法一致
public String HandleException(Integer id){
return "系统繁忙…………请稍后再试";
}
}默认服务降级方法
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") //全局的
public class OrderHystrixController {
@Resource
private PaymenService PaymenService;
//将会调用全局的兜底方法payment_Global_FallbackMethod
//添加一个注解即可,不需要单独指定兜底方法
@HystrixCommand
public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
int age = 10/0;
String result = PaymenService.paymentInfo_TimeOut(id);
return result;
}
//下面是全局fallback方法
public String payment_Global_FallbackMethod(){
return "Global异常处理信息,请稍后再试,(┬_┬)";
}
}提取服务降级类
没有指定降级时间,则默认1秒降级
//实现openfeign远程调用的接口
@Component
public class PaymentFallbackService implements PaymenService {
@Override
public String paymentInfo_OK(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_OK , (┬_┬)";
}
@Override
public String payment_Timeout(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_TimeOut , (┬_┬)";
}
}
//指定远程调用的微服务名称,以及兜底方法类
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
@Component
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}服务熔断
概述
熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路。
熔断状态

熔断打开:请求不再进行调用当前服务,而是直接调用降级fallbak方法。直到打开时长达到所设时钟则进入熔断半开状态(内部设置时钟一般为MTTR(平均故障处理时间))。
熔断关闭:熔断关闭不会对服务进行熔断,请求正常执行
熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则则认为当前服务恢复正常
断路器属性
缺省是10秒内20次调用并有50%的失败情况,就会启动熔断机制,当开启的时候,所有请求都不会进行转发
一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4和5

- 快照时间窗:断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒。
- 请求总数阈值:在快照时间窗内,必须满足请求总数阈值才有资格熔断。默认20,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开。
- 错误百分比阈值:当请求总数在快照时间窗内超过了阈值,比如发生了30次调用,如果在这30次调用,有15次发生了超时异常,也就是超过50%的错误百分比,在默认设定50%阈值情况下,这时候就会将断路器打开。
代码配置
@HystrixProperty(name = "circuitBreaker.enabled", value = "true")是用于在方法级别配置断路器的属性,通过设置circuitBreaker.enabled属性为true来开启断路器功能。@EnableCircuitBreaker是用于在应用程序级别启用断路器功能,通过在配置类上添加该注解来启用断路器,并将其集成到应用程序中。
//Threshold:门槛,阈值
//Volume:容量体积
//服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
//是否开启断路器
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),
//用于设置断路器请求阈值。当在一个统计窗口内请求次数达到该阈值时,Hystrix 将会开始统计错误百分比来判断是否触发断路器,默认20个
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),
//断路多久以后开始尝试是否恢复,默认5s
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000")
//出错百分比阈值,当达到此阈值后,开始短路。默认50%,
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),
})
public String paymentCircuitBreaker(Integer id){
if (id < 0){
throw new RuntimeException("*****id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();//hutool.cn工具包
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;
}
服务限流、隔离
- 请求并发量大,并且调用服务耗时长,可采用线程池隔离,保证大量容器的线程处于可用状态,不会由于服务本身原因一直处于等待或阻塞状态,快速失败返回;
- 请求并发量大,并且调用服务耗时短(通常是命中缓存等情况),可采用信号量隔离,这类服务的调用通常返回都较快,不会占用容器线程太长时间,而且也减少了线程切换的开销;
资源隔离-线程池
将不同的服务调用分配到独立的线程池中,来防止一个服务的故障影响到其他服务。
- 隔离故障:一个服务的故障不会影响到其他服务。
- 限流:通过限制线程池的大小,可以控制每个服务的最大并发量,防止系统过载。
假设你有一个应用程序,它需要调用两个外部服务:服务A和服务B。
- 如果服务A变得很慢或者挂掉了,而你没有使用线程池隔离,那么整个应用程序所有线程可能都会被A占满。
- 通过使用线程池隔离,即使服务A出问题了,它的线程池会被占满,但服务B的线程池仍然可以正常工作,不会受到影响。
//如果指定threadPoolKey,Hystrix自动创建的线程池将以其命名;如果未指定,则将使用groupKey命名,未指定groupKey,groupKey默认是类型,即默认以类名创建线程池(即一个类所有方法共享一个线程池,也可以自己指定,使得不同方法使用不同线程池,不同类使用相同线程池)
@Service
public class MyService {
@HystrixCommand(groupKey="UserGroup", commandKey = "GetUserByIdCommand",
commandProperties = {
//超时时间,单位毫秒。超时进fallback
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "100"),
//判断熔断的最少请求数,默认是10;只有在一定时间内请求数量达到该值,才会进行成功率的计算
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
//熔断的阈值默认值50,表示在一定时间内有50%的请求处理失败,会触发熔断
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "10"),
},
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 String callServiceA() {
// 调用服务A的逻辑
return "Response from Service A";
}
@HystrixCommand(fallbackMethod = "fallbackForServiceB", threadPoolKey = "serviceBThreadPool")
public String callServiceB() {
// 调用服务B的逻辑
return "Response from Service B";
}
public String fallbackForServiceA() {
return "Fallback response for Service A";
}
public String fallbackForServiceB() {
return "Fallback response for Service B";
}
}信号量限制
信号量隔离本质上并没有做到资源隔离,只是起到了限流的作用。当请求数量超过设定的限制时,新的请求会被直接调用fallback,而不是阻塞。(默认使用的是线程池隔离)
@HystrixCommand(
commandProperties = {
@HystrixProperty(
// 超时时间,默认为1000ms
name = "execution.isolation.thread.timeoutInMilliseconds",
value = "10000"
),
// 隔离策略 默认为Thread(线程池隔离) SEMAPHORE:信号量隔离
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value = "SEMAPHORE"),
// 信号量最大并发
@HystrixProperty(name = HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value = "5"),
},
fallbackMethod = "findOrderByIdFallBack" // 降级方法
)服务监控
Hystrix提供了准实时的调用监控(Hystrix Dashboard:Hystrix 仪表盘),Hystrix会持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行多少请求多少成功,多少失败等。
Dashboard搭建
消费者端
pom文件
<!--新增hystrix dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>yml文件
server:
port: 9001
hystrix:
dashboard:
proxy-stream-allow-list: "*" #允许访问Hystrix仪表板代理流的主机列表。设置为 "*",表示允许所有主机访问
# 只有localhost和192.168.0.1这两个主机可以访问Hystrix仪表板的代理流
# proxy-stream-allow-list: "localhost, 192.168.0.1" 主启动类
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
public static void main(String[] args) {
SpringApplication.run(HystrixDashboardMain9001.class,args);
}
}提供者端
<!--所有provider加入监控包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>Dashboard使用
将以下创建registrationBean方法放到要监控的主启动类内即可
//注意:新版本Hystrix需要在主启动类MainAppHystrix8001中指定监控路径
/**
*此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
*ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
*只要在自己的项目里配置上下面的servlet就可以了
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}<p style="color:red">要在 Hystrix Dashboard 中监控方法,这些方法需要使用 @HystrixCommand 注解进行标记</p>




Resilience4j
- Circuit Breaker只是一套规范和接口,落地实现者是Resilience4J
- 文档:https://spring.io/projects/spring-cloud-circuitbreaker#overview
- 中文文档:https://github.com/lmhmhl/Resilience4j-Guides-Chinese/blob/main/index.md
服务熔断
- Resilience4j默认一秒为超时时间
- 窗口包括时间窗口和计数窗口
- 时间窗口:达到最小统计量后,在指定的时间窗口内的访问中,当执行方法的失败率达到指定值时将进入开启OPEN状态
- 计数窗口:达到最小统计量后,在指定的计数窗口内的访问中,当执行方法的失败率达到指定值时将进入开启OPEN状态
消费者
pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<!--兜底方法的配置必须依赖AOP-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>controller
@RestController
public class OpenFeignController {
@Autowired
private OpenFeignService openFeignService;
@GetMapping("/openfeignTest/{id}")
@CircuitBreaker(name = "cloud-payment-service", fallbackMethod = "myCircuitFallback")
public String openFeign(@PathVariable Integer id){
return openFeignService.openFeign(id);
}
//服务降级后的兜底处理方法,必须和源函数有相同的返回,参数类型,且要多一个throwable类型参数,和sentinel一样
public String myCircuitFallback(Integer id,Throwable t) {
// 这里是容错处理逻辑,返回备用结果
return "myCircuitFallback,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~";
}
}service
@Service
//注册在Eureka中的服务的名称,字母数字及‘-’组合,不能有下划线,不区分大小写
@FeignClient("cloud-payment-service")
public interface OpenFeignService {
@GetMapping("/openfeign")
String openFeign(@RequestParam("id") Integer id);
}yml
server:
port: 80
spring:
application:
name: cloud-consumer-order
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
#优先使用服务ip进行注册
prefer-ip-address: true
service-name: ${spring.application.name}
heartbeat:
enabled: true
openfeign:
client:
config:
default:
#cloud-payment-service:
#连接超时时间,为避免演示出错,讲解完本次内容后设置为20秒
connectTimeout: 20000
#读取超时时间,为避免演示出错,讲解完本次内容后设置为20秒
readTimeout: 20000
httpclient:
hc5:
enabled: true
#开启压缩特性
compression:
request:
enabled: true
min-request-size: 2048
mime-types: text/xml,application/xml,application/json
response:
enabled: true
# 开启circuitbreaker和分组激活 spring.cloud.openfeign.circuitbreaker.enabled
circuitbreaker:
enabled: true
group:
enabled: true #没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后
# feign日志以什么级别监控哪个接口
logging:
level:
com:
atguigu:
cloud:
apis:
PayFeignApi: debug
# Resilience4j CircuitBreaker 按照次数:COUNT_BASED 的例子
# 6次访问中当执行方法的失败率达到50%时CircuitBreaker将进入开启OPEN状态(保险丝跳闸断电)拒绝所有请求。
# 等待5秒后,CircuitBreaker 将自动从开启OPEN状态过渡到半开HALF_OPEN状态,允许一些请求通过以测试服务是否恢复正常。
# 如还是异常CircuitBreaker 将重新进入开启OPEN状态;如正常将进入关闭CLOSE闭合状态恢复正常处理请求。
resilience4j:
circuitbreaker:
configs:
default:
failureRateThreshold: 50 #设置50%的调用失败时打开断路器,超过失败请求百分⽐CircuitBreaker变为OPEN状态。
slidingWindowType: COUNT_BASED # 滑动窗口的类型
slidingWindowSize: 6 #滑动窗⼝的⼤⼩配置COUNT_BASED表示6个请求,配置TIME_BASED表示6秒
minimumNumberOfCalls: 6 #断路器计算失败率或慢调用率之前所需的最小样本(每个滑动窗口周期)。如果minimumNumberOfCalls为10,则必须最少记录10个样本,然后才能计算失败率。如果只记录了9次调用,即使所有9次调用都失败,断路器也不会开启。
automaticTransitionFromOpenToHalfOpenEnabled: true # 是否启用自动从开启状态过渡到半开状态,默认值为true。如果启用,CircuitBreaker将自动从开启状态过渡到半开状态,并允许一些请求通过以测试服务是否恢复正常
waitDurationInOpenState: 5s #从OPEN到HALF_OPEN状态需要等待的时间
permittedNumberOfCallsInHalfOpenState: 2 #半开状态允许的最大请求数,默认值为10。在半开状态下,CircuitBreaker将允许最多permittedNumberOfCallsInHalfOpenState个请求通过,如果其中有任何一个请求失败,CircuitBreaker将重新进入开启状态。
recordExceptions:
- java.lang.Exception
instances:
cloud-payment-service:
baseConfig: default
# Resilience4j CircuitBreaker 按照时间:TIME_BASED 的例子
resilience4j:
timelimiter:
configs:
default:
timeout-duration: 10s #神坑的位置,timelimiter 默认限制远程1s,超于1s就超时异常,配置了降级,就走降级逻辑
circuitbreaker:
configs:
default:
failureRateThreshold: 50 #设置50%的调用失败时打开断路器,超过失败请求百分⽐CircuitBreaker变为OPEN状态。
slowCallDurationThreshold: 2s #慢调用时间阈值,高于这个阈值的视为慢调用并增加慢调用比例。
slowCallRateThreshold: 30 #慢调用百分比峰值,断路器把调用时间⼤于slowCallDurationThreshold,视为慢调用,当慢调用比例高于阈值,断路器打开,并开启服务降级
slidingWindowType: TIME_BASED # 滑动窗口的类型
slidingWindowSize: 2 #滑动窗口的大小配置,配置TIME_BASED表示2秒
minimumNumberOfCalls: 2 #断路器计算失败率或慢调用率之前所需的最小样本(每个滑动窗口周期)。
permittedNumberOfCallsInHalfOpenState: 2 #半开状态允许的最大请求数,默认值为10。
waitDurationInOpenState: 5s #从OPEN到HALF_OPEN状态需要等待的时间
recordExceptions:
- java.lang.Exception
instances:
cloud-payment-service:
baseConfig: default
启动类
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}提供者
yml
server:
port: 8001
spring:
application:
name: cloud-payment-service
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
heartbeat:
enabled: truecontroller
@RestController
@RefreshScope // 动态刷新
public class OpenFeignController {
@Value("${server.port}")
private String port;
@GetMapping("/openfeign")
public String openFeign(Integer id) {
if(id==1)
throw new RuntimeException("故意抛出异常");
return "openFeign:"+port;
}
}服务限流
pom
<!--resilience4j-ratelimiter-->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-ratelimiter</artifactId>
</dependency>yml
####resilience4j ratelimiter 限流的例子
resilience4j:
ratelimiter:
configs:
default:
limitForPeriod: 2 #在一次刷新周期内,允许执行的最大请求数
limitRefreshPeriod: 1s # 限流器每隔limitRefreshPeriod刷新一次,将允许处理的最大请求数量重置为limitForPeriod
timeout-duration: 1 # 线程等待权限的默认等待时间
instances:
cloud-payment-service:
baseConfig: defaultcontroller
@GetMapping(value = "/feign/pay/ratelimit/{id}")
@RateLimiter(name = "cloud-payment-service",fallbackMethod = "myRatelimitFallback")
public String myBulkhead(@PathVariable("id") Integer id)
{
return payFeignApi.myRatelimit(id);
}
public String myRatelimitFallback(Integer id,Throwable t)
{
return "你被限流了,禁止访问/(ㄒoㄒ)/~~";
}服务隔离
信号量舱壁
- 当信号量有空闲时,进入系统的请求会直接获取信号量并开始业务处理。
- 当信号量全被占用时,接下来的请求将会进入阻塞状态,SemaphoreBulkhead提供了一个阻塞计时器。
- 如果阻塞状态的请求在阻塞计时内无法获取到信号量则系统会拒绝这些请求。
- 若请求在阻塞计时内获取到了信号量,那将直接获取信号量并执行相应的业务处理。
pom
<!--resilience4j-bulkhead-->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-bulkhead</artifactId>
</dependency>yml
####resilience4j bulkhead 的例子
resilience4j:
bulkhead:
configs:
default:
maxConcurrentCalls: 2 # 隔离允许并发线程执行的最大数量
maxWaitDuration: 1s # 当达到并发调用数量时,新的线程的阻塞时间,我只愿意等待1秒,过时不候进舱壁兜底fallback
instances:
cloud-payment-service:
baseConfig: default
timelimiter:
configs:
default:
timeout-duration: 20scontroller
@GetMapping(value = "/feign/pay/bulkhead/{id}")
@Bulkhead(name = "cloud-payment-service",fallbackMethod = "myBulkheadFallback",type = Bulkhead.Type.SEMAPHORE)
public String myBulkhead(@PathVariable("id") Integer id)
{
return payFeignApi.myBulkhead(id);
}
public String myBulkheadFallback(Throwable t)
{
return "myBulkheadFallback,隔板超出最大数量限制,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~";
}固定线程池舱壁
固定线程池舱壁使用一个固定线程池和一个等待队列来实现舱壁。
当线程池中存在空闲时,则此时进入系统的请求将直接进入线程池开启新线程或使用空闲线程来处理请求。
当线程池中无空闲时时,接下来的请求将进入等待队列
若等待队列仍然无剩余空间时接下来的请求将直接被拒绝,
在队列中的请求等待线程池出现空闲时,将进入线程池进行业务处理。
ThreadPoolBulkhead只对CompletableFuture方法有效,所以我们必创建返回CompletableFuture类型的方法
yml
####resilience4j bulkhead -THREADPOOL的例子
resilience4j:
timelimiter:
configs:
default:
timeout-duration: 10s #timelimiter默认限制远程1s,超过报错不好演示效果所以加上10秒
thread-pool-bulkhead:
configs:
default:
core-thread-pool-size: 1
max-thread-pool-size: 1
queue-capacity: 1
instances:
cloud-payment-service:
baseConfig: default
# spring.cloud.openfeign.circuitbreaker.group.enabled 请设置为false 新启线程和原来主线程脱离controller
修改类型为type = Bulkhead.Type.THREADPOOL即可
@GetMapping(value = "/feign/pay/bulkhead/{id}")
@Bulkhead(name = "cloud-payment-service",fallbackMethod = "myBulkheadFallback",type = Bulkhead.Type.THREADPOOL)
public String myBulkhead(@PathVariable("id") Integer id)
{
return payFeignApi.myBulkhead(id);
}
public String myBulkheadFallback(Throwable t)
{
return "myBulkheadFallback,隔板超出最大数量限制,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~";
}区别
线程池隔离在调用时会创建一个新的线程池来调用,而信号量隔离则不会,其使用的是共享线程。如果调用时被调用方阻塞了,若使用信号量隔离,那么因为使用共享线程的原因,调用方的所有线程都可能会被占用,而线程池隔离则不会。
- 线程池隔离:使用独立的线程池处理请求,适用于长时间处理的操作,完全隔离调用方和被调用方,但有额外的线程切换开销。
- 信号量隔离:使用信号量限制并发请求数量,适用于短时间处理的操作,实现简单,开销较小,但不能完全隔离调用方和被调用方。
Sentinel
运行: java -jar sentinel-dashboard.jar
- 8080端口不能被占用
- 登录账号密码均为sentinel(哨兵)
项目构建
pom文件
<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>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>yml文件
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
# sentinel的网页的url
dashboard: localhost:8080
# 默认8719,当前微服务应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用HttpServer
port: 8719 主启动类
@SpringBootApplication
@EnableDiscoveryClient
public class SentinelMain {
public static void main(String[] args) {
SpringApplication.run(SentinelMain.class, args);
}
}业务类
@RestController
public class SentinelController{
@GetMapping("/testA")
public String testA() {
return "------testA";
}
@GetMapping("/testB")
public String testB() {
return "------testB";
}
}流控规则

- 资源名:唯一名称,默认请求路径
- 针对来源: Sentinel可以针对调用者进行限流,填写微服务名,默认default (不区分来源)
- 阈值类型/单机阈值:
- QPS(每秒钟的请求数量)︰当调用该api的QPS达到阈值的时候,进行限流。
- 线程数:当调用该api的线程数达到阈值的时候,进行限流
- 是否集群:不需要集群
- 流控模式:
- 直接: api达到限流条件时,直接限流
- 关联:当关联的资源达到阈值时,就限流自己
- 链路:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【api级别的针对来源】
- 流控效果:
- 快速失败:直接失败,抛异常
- Warm Up:根据codeFactor (冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值
- 排队等待:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效
直接/默认模式

- 资源名:访问的URL
- 阈值范围QPS,单击阈值1:一秒钟一个查询,超出就直接-快速失败,报Blocked by Sentinel (flow limiting)
- 并发线程,单击阈值1:只能有一个线程处理,超出就直接-快速失败,报Blocked by Sentinel (flow limiting)
- 超时调用也会出现:Blocked by Sentinel (flow limiting)默认提示信息
关联
当关联资源/testB的QPS阀值超过1时,就限流/testA的REST访问地址,当关联资源到阀值后闲置配置的的资源名。

链路
只记录指定链路上的流量(指定资源从入口资源进来访问本资源的流量,如果达到阈值,就进行限流)(API级别的针对来源)

流控效果
快速失败
超出流控规则限制直接失败,抛出异常:Blocked by Sentinel (flow limiting)
warm up(预热)
公式:阈值除以coldFactor,经过预热时长后才会达到阈值.
默认 coldFactor 为 3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。系统初始化的阈值为10/3约等于3,即阈值刚开始为3;然后过了5秒后阈值才慢慢升高,恢复到10


排队等待
匀速排队,让请求以均匀的速度通过,阈值类型必须设置成QPS,否则无效。
/testB每秒1次请求,超过的话就排队等待,等待的超时时间为20000毫秒。


熔断规则
慢比例调用
设定一个资源的慢调用比例阈值为50%,慢调用阈值为200ms,最小请求数为10个,单位统计时长为10s,熔断窗口期为10s。这意味着,如果在单位统计时长10s内,请求超过最小请求数10个,且该资源的调用中,响应时间超过200ms的调用比例超过了50%,那么接下来的10s内,对该资源的所有调用都会被直接降级,即不进行实际的业务逻辑处理,直接返回预设的降级结果。

异常比例
设定一个资源的异常比例阈值为20%,熔断窗口期为10s,最小请求书为10。这意味着,如果在统计时间窗口内,该资源的调用中,请求数大于10,且抛出异常的调用比例超过了20%,那么接下来的10s内,对该资源的所有调用都会被直接降级。

异常数

热点规则
热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的TopK数据,并对其访问进行限制。
- 商品ID为参数,统计一段时间内最常购买的商品ID并进行限制
- 用户ID为参数,针对一段时间内频繁访问的用户ID进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。

规则配置

代码
@GetMapping("/testHotKey")
//对于热点key为testHotKey的方法,违反规则将会调用兜底方法deal_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";
}默认兜底方法

注意:@SentinelResource处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理。 java运行时报出的运行时异常,@SentinelResource不管。
系统规则
系统保护规则是应用整体维度的,仅对入口流量生效。入口流量指的是进入应用的流量,比如 Web服务或Dubbo服务端接收的请求,都属于入口流量。当系统规则触发时,所有的资源都会被限流
- Load:系统负载的阈值,超过该值则触发系统保护。系统负载是指在一定时间间隔内,运行队列中的平均进程数。这个参数只在 Linux/Unix 系统中有效。
- CPU usage (1.5.0+版本):当系统CPU使用率超过阈值即触发系统保护(取值范围0.0-1.0),比较灵敏。
- 平均RT:当单台机器上所有入口流量的平均响应时间(ms)达到阈值即触发系统保护。
- 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口QPS:当单台机器上所有入口流量的QPS达到阈值即触发系统保护。

@SentinelResource
配置概述
- fallback:针对所有异常,(1.6.0之前的版本,fallback函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理。)
- blockHandler:只处理sentinel定义的失败调用或限制调用,如限流或服务降级,代码抛出的异常,其不会处理。
- 若
blockHandler和fallback都进行了配置,则抛出BlockException时只会进入blockHandler处理逻辑。 - 若配置了
fallback,未配置blockHandler,则抛出BlockException时会调用fallback处理 - 若未配置
blockHandler、fallback,则会将BlockException直接抛出。
- 若
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SentinelResource {
// 资源名称
String value() default "";
// 流量类型,IN 表示进入,OUT 表示出去
EntryType entryType() default EntryType.OUT;
// 资源类型
int resourceType() default 0;
// 指定处理被流控的方法
// 1、访问范围需要是public
// 2、返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为 BlockException
// 3、需要和原方法在同一个类中
String blockHandler() default "";
// 指定处理被流控的类
// 1、对应的函数必需为static函数
Class<?>[] blockHandlerClass() default {};
// 指定处理被降级的方法
// 用于在降级和抛出异常的时候提供fallback 处理逻辑,针对除了exceptionsToIgnore里面排除掉的异常类型外,其他所有异常进行处理。
// 1、返回值类型必须与原函数返回值类型一致;
// 2、方法参数列表需要和原函数一致,或者可以额外多一个Throwable类型的参数用于接收对应的异常。
// 3、fallback 函数默认需要和原方法在同一个类中
String fallback() default "";
// 指定处理被降级的类
// 1、对应的函数必需为static函数
Class<?>[] fallbackClass() default {};
// 默认的处理被降级的方法,若同时配置了fallback和defaultFallback,则只有fallback会生效
// 1、返回值类型必须与原函数返回值类型一致;
// 2、方法参数列表需要为空,或者可以额外多一个Throwable类型的参数用于接收对应的异常
// 3、defaultFallback 函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定fallbackClass为对应的类的class
String defaultFallback() default "";
// 需要跟踪的异常类
Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class};
// 需要忽略的异常类,不会进入fallback的逻辑,而是会原样抛出
Class<? extends Throwable>[] exceptionsToIgnore() default {};
}代码
yml文件
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8719 #默认8719,应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer业务类
@RestController
public class RateLimitController{
@GetMapping("/byResource")
@SentinelResource(value = "byResource",blockHandler = "handleException")
public CommonResult byResource(){
return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));
}
//只有违反限流规则才会调用该方法,程序出异常不会调用,比原参数多一个BlockException参数
public CommonResult handleException(BlockException exception){
return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
}
}主启动类
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401{
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class, args);
}
}流控效果
通过@SentinelResource指定,资源名与Value值相同,则会访问指定的降级方法

通过URL,则会返回系统默认的降级方法

统一自定义限流逻辑
创建兜底方法类
public class CustomerBlockHandler {
//必须是公共的静态方法,且参数类型及返回值类型与调用兜底方法的方法一致,最后一个参数为BlockException
public static CommonResult handleException(BlockException exception){
return new CommonResult(2020,"自定义限流处理信息.... CustomerBlockHandler --- 1");
}
public static CommonResult handleException2(BlockException exception){
return new CommonResult(2020,"自定义限流处理信息.... CustomerBlockHandler --- 2");
}
}控制类
@GetMapping("/rateLimit/customerBlockHandler")
@SentinelResource(value = "customerBlockHandler",
//兜底方法类 兜底方法类中的方法
blockHandlerClass =CustomerBlockHandler.class, blockHandler = "handleException2")
public CommonResult customerBlockHandler(){
return new CommonResult(200,"按客戶自定义",new Payment(2020L,"serial003"));
}规则持久化
一旦我们重启应用,Sentinel规则将消失,生产环境需要将配置规则进行持久化。将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上Sentinel上的流控规则持续有效
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
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
#flow:流量控制规则 degrade:熔断降级规则 param-flow:热点规则 system:系统规则 authority:访问控制规则
rule-type: flow添加Nacos配置
流控规则
[
{
// 资源名
"resource": "/test",
// 针对来源,若为 default 则不区分调用来源
"limitApp": "default",
// 限流阈值类型(1:QPS;0:并发线程数)
"grade": 1,
// 阈值
"count": 1,
// 是否是集群模式
"clusterMode": false,
// 流控效果(0:快速失败;1:Warm Up(预热模式);2:排队等待)
"controlBehavior": 0,
// 流控模式(0:直接;1:关联;2:链路)
"strategy": 0,
// 预热时间(秒,预热模式需要此参数)
"warmUpPeriodSec": 10,
// 超时时间(排队等待模式需要此参数)
"maxQueueingTimeMs": 500,
// 关联资源、入口资源(关联、链路模式)
"refResource": "rrr"
}
]降级规则
[
{
// 资源名
"resource": "/test1",
"limitApp": "default",
// 熔断策略(0:慢调用比例,1:异常比率,2:异常计数)
"grade": 0,
// 最大RT、比例阈值、异常数
"count": 200,
// 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入)
"slowRatioThreshold": 0.2,
// 最小请求数
"minRequestAmount": 5,
// 当单位统计时长(类中默认1000)
"statIntervalMs": 1000,
// 熔断时长
"timeWindow": 10
}
]热点规则
[
{
// 资源名
"resource": "/test1",
// 限流模式(QPS 模式,不可更改)
"grade": 1,
// 参数索引
"paramIdx": 0,
// 单机阈值
"count": 13,
// 统计窗口时长
"durationInSec": 6,
// 是否集群 默认false
"clusterMode": 默认false,
//
"burstCount": 0,
// 集群模式配置
"clusterConfig": {
//
"fallbackToLocalWhenFail": true,
//
"flowId": 2,
//
"sampleCount": 10,
//
"thresholdType": 0,
//
"windowIntervalMs": 1000
},
// 流控效果(支持快速失败和匀速排队模式)
"controlBehavior": 0,
//
"limitApp": "default",
//
"maxQueueingTimeMs": 0,
// 高级选项
"paramFlowItemList": [
{
// 参数类型
"classType": "int",
// 限流阈值
"count": 222,
// 参数值
"object": "2"
}
]
}
]系统规则
[
{
// RT
"avgRt": 1,
// CPU 使用率
"highestCpuUsage": -1,
// LOAD
"highestSystemLoad": -1,
// 线程数
"maxThread": -1,
// 入口 QPS
"qps": -1
}
]授权规则
[
{
// 资源名
"resource": "sentinel_spring_web_context",
// 流控应用
"limitApp": "/test",
// 授权类型(0代表白名单;1代表黑名单。)
"strategy": 0
}
]整合Gateway
pom文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-transport-simple-http</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
<scope>compile</scope>
</dependency>yml文件
server:
port: 9528
spring:
application:
name: cloudalibaba-sentinel-gateway # sentinel+gataway整合Case
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
- id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:9001 #匹配后提供服务的路由地址
predicates:
- Path=/pay/** # 断言,路径相匹配的进行路由启动类
@SpringBootApplication
@EnableDiscoveryClient
public class Main9528
{
public static void main(String[] args)
{
SpringApplication.run(Main9528.class,args);
}
}配置类
https://github.com/alibaba/Sentinel/wiki/网关限流#spring-cloud-gateway
/**
* @auther zzyy
* @create 2023-12-01 15:38
* 使用时只需注入对应的 SentinelGatewayFilter 实例以及 SentinelGatewayBlockExceptionHandler 实例即可
*/
@Configuration
public class GatewayConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer)
{
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// Register the block exception handler for Spring Cloud Gateway.
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
@PostConstruct //javax.annotation.PostConstruct
public void doInit() {
initBlockHandler();
}
//处理/自定义返回的例外信息
private void initBlockHandler() {
Set<GatewayFlowRule> rules = new HashSet<>();
//设置流控规则
// 创建一个新的网关流规则,规则的 ID 为 "aliyun_route"
rules.add(new GatewayFlowRule("aliyun_route")
// 设置规则的计数,即每秒最多的请求次数为 10
.setCount(10)
// 设置规则的间隔时间,即每秒
.setIntervalSec(1)
);
// 创建一个新的网关流规则,规则的 ID 为 "aliyun_route"
rules.add(new GatewayFlowRule("aliyun_route")
// 设置规则的计数,即每 2 秒最多的请求次数为 2
.setCount(2)
// 设置规则的间隔时间,即每 2 秒
.setIntervalSec(2)
// 设置超过阈值的请求会被排队等待的数量
.setBurst(2)
// 设置参数项,这个规则是基于客户端 IP 的
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
)
);
// 创建一个新的网关流规则,规则的 ID 为 "httpbin_route"
rules.add(new GatewayFlowRule("httpbin_route")
// 设置规则的计数,即每秒最多的请求次数为 10
.setCount(10)
// 设置规则的间隔时间,即每秒
.setIntervalSec(1)
// 设置控制行为,即超过阈值的请求会被限流
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
// 设置最大排队等待时间,单位为毫秒
.setMaxQueueingTimeoutMs(600)
// 设置参数项,这个规则是基于请求头 "X-Sentinel-Flag" 的
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
.setFieldName("X-Sentinel-Flag")
)
);
// 创建一个新的网关流规则,规则的 ID 为 "httpbin_route"
rules.add(new GatewayFlowRule("httpbin_route")
// 设置规则的计数,即每秒最多的请求次数为 1
.setCount(1)
// 设置规则的间隔时间,即每秒
.setIntervalSec(1)
// 设置参数项,这个规则是基于 URL 参数 "pa" 的
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
.setFieldName("pa")
)
);
// 创建一个新的网关流规则,规则的 ID 为 "httpbin_route"
rules.add(new GatewayFlowRule("httpbin_route")
// 设置规则的计数,即每 30 秒最多的请求次数为 2
.setCount(2)
// 设置规则的间隔时间,即每 30 秒
.setIntervalSec(30)
// 设置参数项,这个规则是基于 URL 参数 "type" 的,只有当 "type" 包含 "warn" 时,这个规则才会生效
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
.setFieldName("type")
.setPattern("warn")
.setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_CONTAINS)
)
);
// 创建一个新的网关流规则,规则的 ID 为 "some_customized_api"
rules.add(new GatewayFlowRule("some_customized_api")
// 设置资源模式,这个规则的资源模式为自定义 API 名称
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
// 设置规则的计数,即每秒最多的请求次数为 5
.setCount(5)
// 设置规则的间隔时间,即每秒
.setIntervalSec(1)
// 设置参数项,这个规则是基于 URL 参数 "pn" 的
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
.setFieldName("pn")
)
);
GatewayRuleManager.loadRules(rules);
//设置超时规则
DegradeRule degradeRule = new DegradeRule();
degradeRule.setResource("consumer-test"); // 设置资源名
degradeRule.setCount(10.0); // 设置阈值
degradeRule.setTimeWindow(10); // 设置熔断窗口,单位为秒
degradeRule.setGrade(1); // 设置熔断策略,0表示平均响应时间,1表示异常比例
DegradeRuleManager.loadRules(Collections.singletonList(degradeRule)); // 加载规则
BlockRequestHandler handler = (exchange, t) -> {
Map<String,String> map = new HashMap<>();
map.put("errorCode", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
map.put("errorMessage", "请求太过频繁,系统忙不过来,触发限流(sentinel+gataway整合Case)");
return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(map));
};
GatewayCallbackManager.setBlockHandler(handler);
}
}