-
Notifications
You must be signed in to change notification settings - Fork 1
/
SchedulingTestRetryTask.java
216 lines (180 loc) · 9.19 KB
/
SchedulingTestRetryTask.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package com.wangji92.retry.springretryexample.task;
import com.github.rholder.retry.*;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.SimpleTimeLimiter;
import com.google.common.util.concurrent.TimeLimiter;
import com.wangji92.retry.springretryexample.service.RetryTestService;
import com.wangji92.retry.springretryexample.utils.AopTargetUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.retry.RecoveryCallback;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor;
import org.springframework.retry.interceptor.RetryOperationsInterceptor;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Nonnull;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* @author 汪小哥
* @date 02-04-2021
*/
@Component
@Slf4j
public class SchedulingTestRetryTask {
private static final int DELAY_TIME = 5000;
@Autowired
private RetryTestService retryTestService;
@Autowired
private RetryListener retryListener;
/**
* aop的方式测试
*/
@ConditionalOnExpression("#{'true'.equals(environment['aopSpringRetry'])}")
@Configuration
public class AopSpringRetry {
@Scheduled(fixedRate = 30000)
public void retryTestService() {
int responseBody = retryTestService.retryTestService();
log.info("retryTestService response result is {}", responseBody);
}
}
/**
* 编程的方式 测试 retry
* {@link AnnotationAwareRetryOperationsInterceptor#getDelegate(java.lang.Object, java.lang.reflect.Method)}
* {@link RetryOperationsInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)}
*/
@ConditionalOnExpression("#{'true'.equals(environment['programmingSpringRetry'])}")
@Configuration
public class ProgrammingSpringRetry {
@Scheduled(fixedRate = 30000)
public void programmingSpringRetry() throws Exception {
// 获取原始的对象
RetryTestService targetRetryTestService = (RetryTestService) AopTargetUtils.getTarget(retryTestService);
RetryTemplate retryTemplate = RetryTemplate.builder()
.maxAttempts(2)
.fixedBackoff(DELAY_TIME)
.retryOn(RemoteAccessException.class)
.traversingCauses()
// 非必须
.withListener(retryListener)
.build();
Integer responseBody = retryTemplate.execute(new RetryCallback<Integer, RemoteAccessException>() {
@Override
public Integer doWithRetry(RetryContext context) throws RemoteAccessException {
return targetRetryTestService.retryTestService();
}
}, new RecoveryCallback<Integer>() {
// 垫底方案
@Override
public Integer recover(RetryContext context) throws Exception {
return targetRetryTestService.recover((RemoteAccessException) context.getLastThrowable());
}
});
log.info("programmingRetry retryTestService response result is {}", responseBody);
}
}
/**
* guava
*/
@ConditionalOnExpression("#{'true'.equals(environment['programmingGuavaRetry'])}")
@Configuration
public class ProgrammingGuavaRetry {
@Scheduled(fixedRate = 30000)
public void programmingGuavaRetry() throws Exception {
// 获取原始的对象
RetryTestService targetRetryTestService = (RetryTestService) AopTargetUtils.getTarget(retryTestService);
// RetryerBuilder 构建重试实例 guavaRetryer,可以设置重试源且可以支持多个重试源,可以配置重试次数或重试超时时间,以及可以配置等待时间间隔
Retryer<Integer> guavaRetryer = RetryerBuilder.<Integer>newBuilder()
//设置异常重试源 根据异常 也可以 retryIfResult 根据结果
.retryIfExceptionOfType(RemoteAccessException.class)
//设置等待间隔时间
.withWaitStrategy(WaitStrategies.fixedWait(5, TimeUnit.SECONDS))
//设置最大重试次数
.withStopStrategy(StopStrategies.stopAfterAttempt(2))
.build();
Integer responseBody = null;
try {
responseBody = guavaRetryer.call(targetRetryTestService::retryTestService);
log.info("guava retry retryTestService response result is {}", responseBody);
} catch (Exception e) {
log.info("guava retry error", e);
responseBody = targetRetryTestService.recover(null);
}
log.info("guava retry retryTestService response result is {}", responseBody);
}
}
/**
* guava withAttemptTimeLimiter 这里如果多线程支持 会导致很多的问题 上下文传递、而且框架没有人升级了
*/
@ConditionalOnExpression("#{'true'.equals(environment['programmingGuavaRetryLimitTime'])}")
@Configuration
public class ProgrammingGuavaRetryLimitTime {
@Scheduled(fixedRate = 30000)
public void programmingGuavaRetry() throws Exception {
// 获取原始的对象
RetryTestService targetRetryTestService = (RetryTestService) AopTargetUtils.getTarget(retryTestService);
// RetryerBuilder 构建重试实例 guavaRetryer,可以设置重试源且可以支持多个重试源,可以配置重试次数或重试超时时间,以及可以配置等待时间间隔
Retryer<Integer> guavaRetryer = RetryerBuilder.<Integer>newBuilder()
//设置异常重试源 根据异常 也可以 retryIfResult 根据结果
.retryIfExceptionOfType(RemoteAccessException.class)
// 【这里将会使用多线程执行(other 线程执行、会导致事务问题、线程上下文传递问题 一定要小心)】 还有这个框架 这个属性高版本不支持了.
.withAttemptTimeLimiter(new FixedAttemptTimeLimit<Integer>(1, TimeUnit.MINUTES))
//设置等待间隔时间
.withWaitStrategy(WaitStrategies.fixedWait(5, TimeUnit.SECONDS))
//设置最大重试次数
.withStopStrategy(StopStrategies.stopAfterAttempt(2))
.build();
Integer responseBody = null;
try {
log.info("guava retry retryTestService begin thread name={}", Thread.currentThread().getName());
responseBody = guavaRetryer.call(() -> {
log.info("current thread name={}", Thread.currentThread().getName());
return targetRetryTestService.retryTestService();
});
log.info("guava retry retryTestService response result is {}", responseBody);
} catch (Exception e) {
log.info("guava retry error", e);
responseBody = targetRetryTestService.recover(null);
}
log.info("guava retry retryTestService response result is {}", responseBody);
}
/**
* {@literal https://github.com/rholder/guava-retrying/issues/66 这个框架没有人支持升级}
* guava retry 这里过期了..
*
* @param <V>
*/
private final class FixedAttemptTimeLimit<V> implements AttemptTimeLimiter<V> {
private final TimeLimiter timeLimiter;
private final long duration;
private final TimeUnit timeUnit;
public FixedAttemptTimeLimit(long duration, @Nonnull TimeUnit timeUnit) {
this(SimpleTimeLimiter.create(Executors.newFixedThreadPool(10)), duration, timeUnit);
}
public FixedAttemptTimeLimit(long duration, @Nonnull TimeUnit timeUnit, @Nonnull ExecutorService executorService) {
this(SimpleTimeLimiter.create(executorService), duration, timeUnit);
}
private FixedAttemptTimeLimit(@Nonnull TimeLimiter timeLimiter, long duration, @Nonnull TimeUnit timeUnit) {
Preconditions.checkNotNull(timeLimiter);
Preconditions.checkNotNull(timeUnit);
this.timeLimiter = timeLimiter;
this.duration = duration;
this.timeUnit = timeUnit;
}
@Override
public V call(Callable<V> callable) throws Exception {
return timeLimiter.callWithTimeout(callable, duration, timeUnit);
}
}
}
}