48cd3c63 by 李墨

[feat]:使用 resilience4j代码实例

1 parent b9c76e85
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>hcom.seektrut</groupId>
<artifactId>spring-boot-resilience4j</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-resilience4j</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<!-- 2.4.12 -->
<!-- <version>2.3.12.RELEASE</version>-->
<version>2.4.12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 1.引入 resilience4j-spring-boot2 和 spring-boot-starter-aop -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 引入监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
</dependencies>
<build>
<finalName>spring-boot-resilience4j</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.seektruth.demo.spring.boot.resilience4j.BootStrap</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <!-- 打jar包 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<excludes>
<exclude>**/*.properties</exclude>
</excludes>
</configuration>
</plugin>
<plugin> <!-- 打源码 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.4</version>
<configuration>
<attach>true</attach>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
package com.seektruth.demo.spring.boot.resilience4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextClosedEvent;
/**
* spring boot resilience4j demo 启动类
*/
@SpringBootApplication
public class BootStrap {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(BootStrap.class,args);
boolean running = ctx.isRunning();
if(running){
System.out.println("启动已启动");
}
}
}
package com.seektruth.demo.spring.boot.resilience4j.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
/**
* 操作日志切面
*/
@Aspect
public class OperateLogAspect implements Ordered {
private static final Logger LOG = LoggerFactory.getLogger(OperateLogAspect.class);
/**
* 设置切入点:横切所有 controller 类的所有方法
*/
@Pointcut("execution(* com.seektruth.demo.spring.boot.resillience4j.controller.*.*(..))")
public void operateLogPointcut() {
// 该方法无方法体,主要为了让同类中其他方法使用此切入点
}
/**
* 配置环绕通知,打印入参日志
*
* @param joinPoint 连接点
*/
@Around("operateLogPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
LOG.info("========================================");
LOG.info(" 操作日志打印:调用 {} {} 方法,请求参数:{}",className,methodName,args);
LOG.info("========================================");
return joinPoint.proceed(args);
}
@Override
public int getOrder() {
// 设置切面先后顺序
return Ordered.HIGHEST_PRECEDENCE;
}
}
package com.seektruth.demo.spring.boot.resilience4j.config;
import io.github.resilience4j.circuitbreaker.CallNotPermittedException;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 全局异常处理类
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
@org.springframework.web.bind.annotation.ExceptionHandler(CallNotPermittedException.class)
public CallNotPermittedException callNotPermittedException(CallNotPermittedException ex) {
System.out.println("全局处理异常");
return ex;
}
}
package com.seektruth.demo.spring.boot.resilience4j.config;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.core.registry.EntryAddedEvent;
import io.github.resilience4j.core.registry.EntryRemovedEvent;
import io.github.resilience4j.core.registry.EntryReplacedEvent;
import io.github.resilience4j.core.registry.RegistryEventConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* RegistryEvent 配置类
*/
@Configuration
public class RegistryEventBeanConfig {
private static final Logger LOG = LoggerFactory.getLogger(RegistryEventBeanConfig.class);
// 熔断器注册时间消费者
@Bean
public RegistryEventConsumer<CircuitBreaker> myRegistryEventConsumer() {
return new RegistryEventConsumer<CircuitBreaker>() {
@Override
public void onEntryAddedEvent(EntryAddedEvent<CircuitBreaker> entryAddedEvent) {
// 打印熔断器事件
entryAddedEvent.getAddedEntry().getEventPublisher().onEvent(
event -> LOG.info("触发 CircuitBreaker onEntryAddedEvent事件:{}", event.toString())
);
}
@Override
public void onEntryRemovedEvent(EntryRemovedEvent<CircuitBreaker> entryRemoveEvent) {
}
@Override
public void onEntryReplacedEvent(EntryReplacedEvent<CircuitBreaker> entryReplacedEvent) {
}
};
}
}
package com.seektruth.demo.spring.boot.resilience4j.controller;
import io.github.resilience4j.bulkhead.BulkheadFullException;
import io.github.resilience4j.circuitbreaker.CallNotPermittedException;
import io.github.resilience4j.ratelimiter.RequestNotPermitted;
import io.github.resilience4j.retry.MaxRetriesExceededException;
/**
* 降级方法
*/
public class BaseController {
public String fallback(CallNotPermittedException ex){
return "服务已熔断。";
}
public String fallback(BulkheadFullException ex){
return "服务并发限制,不允许访问。";
}
public String fallback(RequestNotPermitted ex){
return "限速了不允许访问了。";
}
public String fallback(MaxRetriesExceededException ex){
return "重试过多,不允许访问。";
}
public String fallback(Throwable ex){
return "服务降级了。";
}
}
package com.seektruth.demo.spring.boot.resilience4j.controller;
import com.seektruth.demo.spring.boot.resilience4j.service.BusinessService;
import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import io.github.resilience4j.retry.annotation.Retry;
import io.github.resilience4j.timelimiter.annotation.TimeLimiter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 业务控制器
*/
@RestController
@RequestMapping("/business")
public class BusinessController extends BaseController {
@Autowired
private BusinessService service;
@Bulkhead(name="bulkheadInstance",type = Bulkhead.Type.SEMAPHORE,fallbackMethod = "fallback")
@RequestMapping("/bulkhead/semaphore")
public String bulkheadSemaphore(){
return service.query();
}
@Bulkhead(name="bulkheadInstance",type = Bulkhead.Type.THREADPOOL,fallbackMethod = "fallback")
@RequestMapping("/bulkhead/thread")
public String bulkheadThread(){
return service.query();
}
@CircuitBreaker(name="circuitbreakerCountBased",fallbackMethod = "fallback")
@RequestMapping("/circuitBreaker/exception")
public String circuitBreakerException(){
return service.query();
}
@CircuitBreaker(name="circuitbreakerTimeBased",fallbackMethod = "fallback")
@RequestMapping("/circuitBreaker/slowcall")
public String circuitBreakerSlowCall(){
return service.query();
}
@RateLimiter(name="ratelimiterInstance", fallbackMethod = "fallback")
@RequestMapping("/ratelimiter")
public String rateLimiter(){
return service.query();
}
@Retry(name="retryinInstance", fallbackMethod = "fallback")
@RequestMapping("/retry")
public String retry(){
return service.query();
}
@TimeLimiter(name="timelimiterInstance", fallbackMethod = "fallback")
@RequestMapping("/timelimit")
public String timelimit(){
return service.query();
}
}
package com.seektruth.demo.spring.boot.resilience4j.exception;
/**
* 业务处理异常
*/
public class ResultException extends RuntimeException{
}
package com.seektruth.demo.spring.boot.resilience4j.service;
public interface BusinessService {
public String query();
}
package com.seektruth.demo.spring.boot.resilience4j.service.impl;
import com.seektruth.demo.spring.boot.resilience4j.service.BusinessService;
import org.springframework.stereotype.Service;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
/**
* 业务处理实现类
*/
@Service
public class BusinessServiceImpl implements BusinessService {
@Override
public String query() {
randomSleep();
return "当前业务正常。";
}
public void randomSleep(){
Integer time = ThreadLocalRandom.current().nextInt(10);
try {
TimeUnit.SECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前业务处理时长:"+time+"秒");
}
}
# 端口
server:
port: 9080
# 应用名
spring:
application:
name: spring-boot-resilience4j-demo
# 监控信息配置
management:
endpoints:
web:
exposure:
include: '*'
health:
show-details: always
diskspace:
enabled: false
circuitbreakers:
enabled: true
ratelimiters:
enabled: false
metrics:
tags:
application: ${spring.application.name}
distribution:
percentiles-histogram:
http:
server:
requests: true
resilience4j:
circuitbreaker:
calls: true
# resillience4j 舱壁配置
resilience4j.bulkhead:
configs:
base:
# 最大并发调用数量
maxConcurrentCalls: 2
# 线程尝试进入舱壁的最大等待时间(单位:毫秒)
maxWaitDuration: 10000
#
writableStackTraceEnabled: true
# 时间消费者缓冲区
eventConsumerBufferSize: 10
instances:
bulkheadInstance:
# 指定使用 base 配置
baseConfig: base
# resillience4j 熔断器配置
resilience4j.circuitbreaker:
configs:
basecount:
# 滑动窗口类型:COUNT_BASED(基于计数),TIME_BASED(基于时间)
slidingWindowType: COUNT_BASED
# 窗口大小(滑动窗口类型是COUNT_BASED,单位为个;滑动窗口类型是TIME_BASED,单位为秒)
slidingWindowSize: 10
# 失败率百分比权重(50%),大于等于此值则熔断器打开
failureRateThreshold: 50
# 慢调用百分比(认为所有调用时间超过 slowCallDurationThreshold的都是慢调用),大于等于此值则熔断器打开
slowCallRateThreshold: 50
# 调用持续多长时间被认定为慢调用,并增加慢调用比例
slowCallDurationThreshold: 1100
# 熔断器状态为半打开时,允许的调用数量
permittedNumberOfCallsInHalfOpenState: 2
# 半打开状态等待的最大持续时间(0代表无限等待直到允许的调用都完成)
#maxWaitDurationInHalfOpenState: 0
# 计算失败率或慢调用前,要求的最小调用数量
minimumNumberOfCalls: 2
# 熔断器状态由 OPEN 到 HALF_OPEN 等待的时间
waitDurationInOpenState: 60000
# 熔断器状态是否自动从 OPEN 转到 HALF_OPEN。true:自动(会使用1个线程去自动切换状态);false:当有新的调用进来时再进行转换
automaticTransitionFromOpenToHalfOpenEnabled: false
recordExceptions:
- java.lang.Exception
ignoreExceptions:
- com.seektruth.demo.spring.boot.resilience4j.exception.ResultException
# 定制化 Predicate 用于判断此异常是否为失败调用。
#recordFailurePredicate: org.spring.boot.resilience4j.exception.RecordFailurePredicate
# 定制化 Predicate 用于判断此异常是否被忽略。
#ignoreExceptionPredicate:
basetime:
# 滑动窗口类型:COUNT_BASED(基于计数),TIME_BASED(基于时间)
slidingWindowType: TIME_BASED
# 窗口大小(滑动窗口类型是COUNT_BASED,单位为个;滑动窗口类型是TIME_BASED,单位为秒)
slidingWindowSize: 10
# 失败率百分比权重(50%),大于等于此值则熔断器打开
failureRateThreshold: 50
# 慢调用百分比(认为所有调用时间超过 slowCallDurationThreshold的都是慢调用),大于等于此值则熔断器打开
slowCallRateThreshold: 50
# 调用持续多长时间被认定为慢调用,并增加慢调用比例
slowCallDurationThreshold: 1100
# 熔断器状态为半打开时,允许的调用数量
permittedNumberOfCallsInHalfOpenState: 2
# 半打开状态等待的最大持续时间(0代表无限等待直到允许的调用都完成)
#maxWaitDurationInHalfOpenState: 0
# 计算失败率或慢调用前,要求的最小调用数量
minimumNumberOfCalls: 2
# 熔断器状态由 OPEN 到 HALF_OPEN 等待的时间
waitDurationInOpenState: 60000
# 熔断器状态是否自动从 OPEN 转到 HALF_OPEN。true:自动(会使用1个线程去自动切换状态);false:当有新的调用进来时再进行转换
automaticTransitionFromOpenToHalfOpenEnabled: false
recordExceptions:
- java.lang.Exception
ignoreExceptions:
- com.seektruth.demo.spring.boot.resilience4j.exception.ResultException
# 定制化 Predicate 用于判断此异常是否为失败调用。
#recordFailurePredicate: org.spring.boot.resilience4j.exception.RecordFailurePredicate
# 定制化 Predicate 用于判断此异常是否被忽略。
#ignoreExceptionPredicate:
instances:
circuitbreakerCountBased:
baseConfig: basecount
circuitbreakerTimeBased:
baseConfig: basetime
# resilience4j RateLimiter
resilience4j.ratelimiter:
configs:
base:
# 在一个limit刷新周期内,可用的许可数量
limitForPeriod: 50
# limit 刷新的周期(单位:纳秒)
limitRefreshPeriod: 500
# 线程等待许可的等待时间(单位:秒)
timeoutDuration: 2
# subscribeForEvents:
# allowHealthIndicatorToFail:
# registerHealthIndicator:
# eventConsumerBufferSize:
# writableStackTraceEnabled:
instances:
ratelimiterInstance:
baseConfig: base
# resilience4j Retry
resilience4j.retry:
configs:
base:
# 等待下次重试时间
waitDuration:
# 根据尝试次数和结果或异常修改失败后的等待间隔的函数。
# intervalBiFunction:
# 最大重试次数
maxAttempts: 2
# 断言异常是否需要重试
# retryExceptionPredicate:
# 断言返回的结果是否需要重试
# resultPredicate:
# 需要重试的 异常列表
retryExceptions:
- java.lang.Exception
# 忽略重试的 异常列表
ignoreExceptions:
- java.lang.Exception
# 重试事件缓冲区大小
eventConsumerBufferSize: 10
# 开启指数回退策略
enableExponentialBackoff: false
# 指数回退的乘数
# exponentialBackoffMultiplier:
# 指数回退最大间隔
# exponentialMaxWaitDuration:
# 开启或关闭随机延迟策略
enableRandomizedWait: false
# 随机延迟因子
# randomizedWaitFactor:
instances:
retryinInstance:
baseConfig: base
# resilience4j TimeLimiter
resilience4j.TimeLimiter:
configs:
base:
# 超时时间(单位:秒)
timeoutDuration: 2
# 取消正在执行的 Future
cancelRunningFuture: true
# 超时事件缓冲区大小
eventConsumerBufferSize: 10
instances:
timelimiterInstance:
baseConfig: base
\ No newline at end of file
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!