48cd3c63 by 李墨

[feat]:使用 resilience4j代码实例

1 parent b9c76e85
1 <?xml version="1.0"?>
2 <project
3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
4 xmlns="http://maven.apache.org/POM/4.0.0"
5 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
6 <modelVersion>4.0.0</modelVersion>
7 <groupId>hcom.seektrut</groupId>
8 <artifactId>spring-boot-resilience4j</artifactId>
9 <version>0.0.1-SNAPSHOT</version>
10 <name>spring-boot-resilience4j</name>
11 <url>http://maven.apache.org</url>
12 <properties>
13 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
14 </properties>
15
16 <dependencyManagement>
17 <dependencies>
18 <dependency>
19 <groupId>org.springframework.boot</groupId>
20 <artifactId>spring-boot-dependencies</artifactId>
21 <!-- 2.4.12 -->
22 <!-- <version>2.3.12.RELEASE</version>-->
23 <version>2.4.12</version>
24 <type>pom</type>
25 <scope>import</scope>
26 </dependency>
27 </dependencies>
28 </dependencyManagement>
29
30 <dependencies>
31 <!-- 1.引入 resilience4j-spring-boot2 和 spring-boot-starter-aop -->
32 <dependency>
33 <groupId>io.github.resilience4j</groupId>
34 <artifactId>resilience4j-spring-boot2</artifactId>
35 <version>1.7.0</version>
36 </dependency>
37 <dependency>
38 <groupId>org.springframework.boot</groupId>
39 <artifactId>spring-boot-starter-web</artifactId>
40 </dependency>
41 <dependency>
42 <groupId>org.springframework.boot</groupId>
43 <artifactId>spring-boot-starter-aop</artifactId>
44 </dependency>
45
46 <!-- 引入监控 -->
47 <dependency>
48 <groupId>org.springframework.boot</groupId>
49 <artifactId>spring-boot-starter-actuator</artifactId>
50 </dependency>
51 <dependency>
52 <groupId>org.springframework.boot</groupId>
53 <artifactId>spring-boot-configuration-processor</artifactId>
54 </dependency>
55 </dependencies>
56
57 <build>
58 <finalName>spring-boot-resilience4j</finalName>
59 <plugins>
60 <plugin>
61 <groupId>org.springframework.boot</groupId>
62 <artifactId>spring-boot-maven-plugin</artifactId>
63 <configuration>
64 <mainClass>com.seektruth.demo.spring.boot.resilience4j.BootStrap</mainClass>
65 </configuration>
66 <executions>
67 <execution>
68 <goals>
69 <goal>repackage</goal>
70 </goals>
71 </execution>
72 </executions>
73 </plugin>
74 <plugin> <!-- 打jar包 -->
75 <groupId>org.apache.maven.plugins</groupId>
76 <artifactId>maven-jar-plugin</artifactId>
77 <version>2.4</version>
78 <configuration>
79 <excludes>
80 <exclude>**/*.properties</exclude>
81 </excludes>
82 </configuration>
83 </plugin>
84 <plugin> <!-- 打源码 -->
85 <groupId>org.apache.maven.plugins</groupId>
86 <artifactId>maven-source-plugin</artifactId>
87 <version>2.4</version>
88 <configuration>
89 <attach>true</attach>
90 </configuration>
91 <executions>
92 <execution>
93 <phase>compile</phase>
94 <goals>
95 <goal>jar</goal>
96 </goals>
97 </execution>
98 </executions>
99 </plugin>
100 <plugin>
101 <groupId>org.apache.maven.plugins</groupId>
102 <artifactId>maven-surefire-plugin</artifactId>
103 <configuration>
104 <skip>true</skip>
105 </configuration>
106 </plugin>
107 <plugin>
108 <groupId>org.apache.maven.plugins</groupId>
109 <artifactId>maven-compiler-plugin</artifactId>
110 <configuration>
111 <source>8</source>
112 <target>8</target>
113 </configuration>
114 </plugin>
115 </plugins>
116 </build>
117 </project>
1 package com.seektruth.demo.spring.boot.resilience4j;
2
3
4 import org.springframework.boot.SpringApplication;
5 import org.springframework.boot.autoconfigure.SpringBootApplication;
6 import org.springframework.context.ApplicationEvent;
7 import org.springframework.context.ApplicationListener;
8 import org.springframework.context.ConfigurableApplicationContext;
9 import org.springframework.context.event.ContextClosedEvent;
10
11 /**
12 * spring boot resilience4j demo 启动类
13 */
14 @SpringBootApplication
15 public class BootStrap {
16
17 public static void main(String[] args) {
18 ConfigurableApplicationContext ctx = SpringApplication.run(BootStrap.class,args);
19 boolean running = ctx.isRunning();
20 if(running){
21 System.out.println("启动已启动");
22 }
23 }
24 }
1 package com.seektruth.demo.spring.boot.resilience4j.aspect;
2
3 import org.aspectj.lang.ProceedingJoinPoint;
4 import org.aspectj.lang.annotation.Around;
5 import org.aspectj.lang.annotation.Aspect;
6 import org.aspectj.lang.annotation.Pointcut;
7 import org.slf4j.Logger;
8 import org.slf4j.LoggerFactory;
9 import org.springframework.core.Ordered;
10
11 /**
12 * 操作日志切面
13 */
14 @Aspect
15 public class OperateLogAspect implements Ordered {
16 private static final Logger LOG = LoggerFactory.getLogger(OperateLogAspect.class);
17
18 /**
19 * 设置切入点:横切所有 controller 类的所有方法
20 */
21 @Pointcut("execution(* com.seektruth.demo.spring.boot.resillience4j.controller.*.*(..))")
22 public void operateLogPointcut() {
23 // 该方法无方法体,主要为了让同类中其他方法使用此切入点
24 }
25
26 /**
27 * 配置环绕通知,打印入参日志
28 *
29 * @param joinPoint 连接点
30 */
31 @Around("operateLogPointcut()")
32 public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
33 String className = joinPoint.getTarget().getClass().getName();
34 String methodName = joinPoint.getSignature().getName();
35 Object[] args = joinPoint.getArgs();
36 LOG.info("========================================");
37 LOG.info(" 操作日志打印:调用 {} {} 方法,请求参数:{}",className,methodName,args);
38 LOG.info("========================================");
39 return joinPoint.proceed(args);
40 }
41
42 @Override
43 public int getOrder() {
44 // 设置切面先后顺序
45 return Ordered.HIGHEST_PRECEDENCE;
46 }
47 }
1 package com.seektruth.demo.spring.boot.resilience4j.config;
2
3 import io.github.resilience4j.circuitbreaker.CallNotPermittedException;
4 import org.springframework.web.bind.annotation.RestControllerAdvice;
5
6 /**
7 * 全局异常处理类
8 */
9 @RestControllerAdvice
10 public class GlobalExceptionHandler {
11
12 @org.springframework.web.bind.annotation.ExceptionHandler(CallNotPermittedException.class)
13 public CallNotPermittedException callNotPermittedException(CallNotPermittedException ex) {
14 System.out.println("全局处理异常");
15 return ex;
16 }
17 }
1 package com.seektruth.demo.spring.boot.resilience4j.config;
2
3 import io.github.resilience4j.circuitbreaker.CircuitBreaker;
4 import io.github.resilience4j.core.registry.EntryAddedEvent;
5 import io.github.resilience4j.core.registry.EntryRemovedEvent;
6 import io.github.resilience4j.core.registry.EntryReplacedEvent;
7 import io.github.resilience4j.core.registry.RegistryEventConsumer;
8 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory;
10 import org.springframework.context.annotation.Bean;
11 import org.springframework.context.annotation.Configuration;
12
13 /**
14 * RegistryEvent 配置类
15 */
16 @Configuration
17 public class RegistryEventBeanConfig {
18
19 private static final Logger LOG = LoggerFactory.getLogger(RegistryEventBeanConfig.class);
20
21 // 熔断器注册时间消费者
22 @Bean
23 public RegistryEventConsumer<CircuitBreaker> myRegistryEventConsumer() {
24
25 return new RegistryEventConsumer<CircuitBreaker>() {
26 @Override
27 public void onEntryAddedEvent(EntryAddedEvent<CircuitBreaker> entryAddedEvent) {
28 // 打印熔断器事件
29 entryAddedEvent.getAddedEntry().getEventPublisher().onEvent(
30 event -> LOG.info("触发 CircuitBreaker onEntryAddedEvent事件:{}", event.toString())
31 );
32 }
33
34 @Override
35 public void onEntryRemovedEvent(EntryRemovedEvent<CircuitBreaker> entryRemoveEvent) {
36
37 }
38
39 @Override
40 public void onEntryReplacedEvent(EntryReplacedEvent<CircuitBreaker> entryReplacedEvent) {
41
42 }
43 };
44 }
45
46 }
1 package com.seektruth.demo.spring.boot.resilience4j.controller;
2
3 import io.github.resilience4j.bulkhead.BulkheadFullException;
4 import io.github.resilience4j.circuitbreaker.CallNotPermittedException;
5 import io.github.resilience4j.ratelimiter.RequestNotPermitted;
6 import io.github.resilience4j.retry.MaxRetriesExceededException;
7
8 /**
9 * 降级方法
10 */
11 public class BaseController {
12
13 public String fallback(CallNotPermittedException ex){
14 return "服务已熔断。";
15 }
16
17 public String fallback(BulkheadFullException ex){
18 return "服务并发限制,不允许访问。";
19 }
20
21 public String fallback(RequestNotPermitted ex){
22 return "限速了不允许访问了。";
23 }
24
25 public String fallback(MaxRetriesExceededException ex){
26 return "重试过多,不允许访问。";
27 }
28
29 public String fallback(Throwable ex){
30 return "服务降级了。";
31 }
32 }
1 package com.seektruth.demo.spring.boot.resilience4j.controller;
2
3 import com.seektruth.demo.spring.boot.resilience4j.service.BusinessService;
4 import io.github.resilience4j.bulkhead.annotation.Bulkhead;
5 import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
6 import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
7 import io.github.resilience4j.retry.annotation.Retry;
8 import io.github.resilience4j.timelimiter.annotation.TimeLimiter;
9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.web.bind.annotation.RequestMapping;
11 import org.springframework.web.bind.annotation.RestController;
12
13 /**
14 * 业务控制器
15 */
16 @RestController
17 @RequestMapping("/business")
18 public class BusinessController extends BaseController {
19
20 @Autowired
21 private BusinessService service;
22
23 @Bulkhead(name="bulkheadInstance",type = Bulkhead.Type.SEMAPHORE,fallbackMethod = "fallback")
24 @RequestMapping("/bulkhead/semaphore")
25 public String bulkheadSemaphore(){
26 return service.query();
27 }
28
29 @Bulkhead(name="bulkheadInstance",type = Bulkhead.Type.THREADPOOL,fallbackMethod = "fallback")
30 @RequestMapping("/bulkhead/thread")
31 public String bulkheadThread(){
32 return service.query();
33 }
34
35 @CircuitBreaker(name="circuitbreakerCountBased",fallbackMethod = "fallback")
36 @RequestMapping("/circuitBreaker/exception")
37 public String circuitBreakerException(){
38 return service.query();
39 }
40
41 @CircuitBreaker(name="circuitbreakerTimeBased",fallbackMethod = "fallback")
42 @RequestMapping("/circuitBreaker/slowcall")
43 public String circuitBreakerSlowCall(){
44 return service.query();
45 }
46
47 @RateLimiter(name="ratelimiterInstance", fallbackMethod = "fallback")
48 @RequestMapping("/ratelimiter")
49 public String rateLimiter(){
50 return service.query();
51 }
52
53 @Retry(name="retryinInstance", fallbackMethod = "fallback")
54 @RequestMapping("/retry")
55 public String retry(){
56 return service.query();
57 }
58
59 @TimeLimiter(name="timelimiterInstance", fallbackMethod = "fallback")
60 @RequestMapping("/timelimit")
61 public String timelimit(){
62 return service.query();
63 }
64
65 }
1 package com.seektruth.demo.spring.boot.resilience4j.exception;
2
3 /**
4 * 业务处理异常
5 */
6 public class ResultException extends RuntimeException{
7
8 }
1 package com.seektruth.demo.spring.boot.resilience4j.service;
2
3 public interface BusinessService {
4
5
6 public String query();
7
8 }
1 package com.seektruth.demo.spring.boot.resilience4j.service.impl;
2
3 import com.seektruth.demo.spring.boot.resilience4j.service.BusinessService;
4 import org.springframework.stereotype.Service;
5
6 import java.util.concurrent.ThreadLocalRandom;
7 import java.util.concurrent.TimeUnit;
8
9 /**
10 * 业务处理实现类
11 */
12 @Service
13 public class BusinessServiceImpl implements BusinessService {
14
15 @Override
16 public String query() {
17 randomSleep();
18 return "当前业务正常。";
19 }
20
21 public void randomSleep(){
22 Integer time = ThreadLocalRandom.current().nextInt(10);
23 try {
24 TimeUnit.SECONDS.sleep(time);
25 } catch (InterruptedException e) {
26 e.printStackTrace();
27 }
28 System.out.println("当前业务处理时长:"+time+"秒");
29 }
30 }
1
2 # 端口
3 server:
4 port: 9080
5 # 应用名
6 spring:
7 application:
8 name: spring-boot-resilience4j-demo
9
10 # 监控信息配置
11 management:
12 endpoints:
13 web:
14 exposure:
15 include: '*'
16 health:
17 show-details: always
18 diskspace:
19 enabled: false
20 circuitbreakers:
21 enabled: true
22 ratelimiters:
23 enabled: false
24 metrics:
25 tags:
26 application: ${spring.application.name}
27 distribution:
28 percentiles-histogram:
29 http:
30 server:
31 requests: true
32 resilience4j:
33 circuitbreaker:
34 calls: true
35
36 # resillience4j 舱壁配置
37 resilience4j.bulkhead:
38 configs:
39 base:
40 # 最大并发调用数量
41 maxConcurrentCalls: 2
42 # 线程尝试进入舱壁的最大等待时间(单位:毫秒)
43 maxWaitDuration: 10000
44 #
45 writableStackTraceEnabled: true
46 # 时间消费者缓冲区
47 eventConsumerBufferSize: 10
48 instances:
49 bulkheadInstance:
50 # 指定使用 base 配置
51 baseConfig: base
52
53 # resillience4j 熔断器配置
54 resilience4j.circuitbreaker:
55 configs:
56 basecount:
57 # 滑动窗口类型:COUNT_BASED(基于计数),TIME_BASED(基于时间)
58 slidingWindowType: COUNT_BASED
59 # 窗口大小(滑动窗口类型是COUNT_BASED,单位为个;滑动窗口类型是TIME_BASED,单位为秒)
60 slidingWindowSize: 10
61 # 失败率百分比权重(50%),大于等于此值则熔断器打开
62 failureRateThreshold: 50
63 # 慢调用百分比(认为所有调用时间超过 slowCallDurationThreshold的都是慢调用),大于等于此值则熔断器打开
64 slowCallRateThreshold: 50
65 # 调用持续多长时间被认定为慢调用,并增加慢调用比例
66 slowCallDurationThreshold: 1100
67 # 熔断器状态为半打开时,允许的调用数量
68 permittedNumberOfCallsInHalfOpenState: 2
69 # 半打开状态等待的最大持续时间(0代表无限等待直到允许的调用都完成)
70 #maxWaitDurationInHalfOpenState: 0
71 # 计算失败率或慢调用前,要求的最小调用数量
72 minimumNumberOfCalls: 2
73 # 熔断器状态由 OPEN 到 HALF_OPEN 等待的时间
74 waitDurationInOpenState: 60000
75 # 熔断器状态是否自动从 OPEN 转到 HALF_OPEN。true:自动(会使用1个线程去自动切换状态);false:当有新的调用进来时再进行转换
76 automaticTransitionFromOpenToHalfOpenEnabled: false
77 recordExceptions:
78 - java.lang.Exception
79 ignoreExceptions:
80 - com.seektruth.demo.spring.boot.resilience4j.exception.ResultException
81
82 # 定制化 Predicate 用于判断此异常是否为失败调用。
83 #recordFailurePredicate: org.spring.boot.resilience4j.exception.RecordFailurePredicate
84 # 定制化 Predicate 用于判断此异常是否被忽略。
85 #ignoreExceptionPredicate:
86 basetime:
87 # 滑动窗口类型:COUNT_BASED(基于计数),TIME_BASED(基于时间)
88 slidingWindowType: TIME_BASED
89 # 窗口大小(滑动窗口类型是COUNT_BASED,单位为个;滑动窗口类型是TIME_BASED,单位为秒)
90 slidingWindowSize: 10
91 # 失败率百分比权重(50%),大于等于此值则熔断器打开
92 failureRateThreshold: 50
93 # 慢调用百分比(认为所有调用时间超过 slowCallDurationThreshold的都是慢调用),大于等于此值则熔断器打开
94 slowCallRateThreshold: 50
95 # 调用持续多长时间被认定为慢调用,并增加慢调用比例
96 slowCallDurationThreshold: 1100
97 # 熔断器状态为半打开时,允许的调用数量
98 permittedNumberOfCallsInHalfOpenState: 2
99 # 半打开状态等待的最大持续时间(0代表无限等待直到允许的调用都完成)
100 #maxWaitDurationInHalfOpenState: 0
101 # 计算失败率或慢调用前,要求的最小调用数量
102 minimumNumberOfCalls: 2
103 # 熔断器状态由 OPEN 到 HALF_OPEN 等待的时间
104 waitDurationInOpenState: 60000
105 # 熔断器状态是否自动从 OPEN 转到 HALF_OPEN。true:自动(会使用1个线程去自动切换状态);false:当有新的调用进来时再进行转换
106 automaticTransitionFromOpenToHalfOpenEnabled: false
107 recordExceptions:
108 - java.lang.Exception
109 ignoreExceptions:
110 - com.seektruth.demo.spring.boot.resilience4j.exception.ResultException
111 # 定制化 Predicate 用于判断此异常是否为失败调用。
112 #recordFailurePredicate: org.spring.boot.resilience4j.exception.RecordFailurePredicate
113 # 定制化 Predicate 用于判断此异常是否被忽略。
114 #ignoreExceptionPredicate:
115 instances:
116 circuitbreakerCountBased:
117 baseConfig: basecount
118 circuitbreakerTimeBased:
119 baseConfig: basetime
120
121 # resilience4j RateLimiter
122 resilience4j.ratelimiter:
123 configs:
124 base:
125 # 在一个limit刷新周期内,可用的许可数量
126 limitForPeriod: 50
127 # limit 刷新的周期(单位:纳秒)
128 limitRefreshPeriod: 500
129 # 线程等待许可的等待时间(单位:秒)
130 timeoutDuration: 2
131 # subscribeForEvents:
132 # allowHealthIndicatorToFail:
133 # registerHealthIndicator:
134 # eventConsumerBufferSize:
135 # writableStackTraceEnabled:
136 instances:
137 ratelimiterInstance:
138 baseConfig: base
139
140 # resilience4j Retry
141 resilience4j.retry:
142 configs:
143 base:
144 # 等待下次重试时间
145 waitDuration:
146 # 根据尝试次数和结果或异常修改失败后的等待间隔的函数。
147 # intervalBiFunction:
148 # 最大重试次数
149 maxAttempts: 2
150 # 断言异常是否需要重试
151 # retryExceptionPredicate:
152 # 断言返回的结果是否需要重试
153 # resultPredicate:
154 # 需要重试的 异常列表
155 retryExceptions:
156 - java.lang.Exception
157 # 忽略重试的 异常列表
158 ignoreExceptions:
159 - java.lang.Exception
160 # 重试事件缓冲区大小
161 eventConsumerBufferSize: 10
162 # 开启指数回退策略
163 enableExponentialBackoff: false
164 # 指数回退的乘数
165 # exponentialBackoffMultiplier:
166 # 指数回退最大间隔
167 # exponentialMaxWaitDuration:
168 # 开启或关闭随机延迟策略
169 enableRandomizedWait: false
170 # 随机延迟因子
171 # randomizedWaitFactor:
172 instances:
173 retryinInstance:
174 baseConfig: base
175
176 # resilience4j TimeLimiter
177 resilience4j.TimeLimiter:
178 configs:
179 base:
180 # 超时时间(单位:秒)
181 timeoutDuration: 2
182 # 取消正在执行的 Future
183 cancelRunningFuture: true
184 # 超时事件缓冲区大小
185 eventConsumerBufferSize: 10
186 instances:
187 timelimiterInstance:
188 baseConfig: base
...\ No newline at end of file ...\ 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!