1.异步编程介绍
什么是异步编程
异步编程是一种非阻塞的编程模式,允许程序在等待某个操作完成时继续执行其他任务,而不是一直等待。
当操作完成后,通过回调函数、Future 或事件通知等方式获取结果。
同步 vs 异步对比:
- 同步:顺序执行,每一步必须等待前一步完成
- 异步:非阻塞执行,可以同时处理多个任务
Java 中的异步实现方式
1. CompletableFuture (Java 8+)
java
// 创建异步任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "异步任务结果";
});
// 处理结果
future.thenAccept(result -> System.out.println("结果: " + result));
2. @Async 注解 (Spring Framework)
java
@Service
public class AsyncService {
@Async
public CompletableFuture<String> asyncMethod() {
// 异步执行的方法
return CompletableFuture.completedFuture("执行结果");
}
}
3. 回调函数
java
public interface Callback {
void onSuccess(String result);
void onError(Exception e);
}
public void asyncOperation(Callback callback) {
new Thread(() -> {
try {
// 模拟操作
String result = doSomething();
callback.onSuccess(result);
} catch (Exception e) {
callback.onError(e);
}
}).start();
}
2.异步编程中的常见错误
2.1 网络相关错误
- 连接超时
- 读取超时
- DNS 解析失败
- 网络不可达
2.2 资源相关错误
- 内存不足
- 线程池耗尽
- 数据库连接超时
2.3 业务逻辑错误
- 远程服务返回错误码
- 数据格式异常
- 业务规则校验失败
2.4 示例:可能出错的异步方法
java
public class UnreliableService {
// 模拟不可靠的远程服务调用
public CompletableFuture<String> callExternalService(String data) {
return CompletableFuture.supplyAsync(() -> {
// 模拟随机错误
double random = Math.random();
if (random < 0.3) {
throw new RuntimeException("网络超时");
} else if (random < 0.6) {
throw new RuntimeException("服务端错误");
}
return "处理结果: " + data;
});
}
}
3. 异步重试机制实现
3.1 手动重试实现
3.1.1 基础重试逻辑
java
public class SimpleRetry {
public static <T> CompletableFuture<T> retryAsync(
Supplier<CompletableFuture<T>> task,
int maxAttempts,
long delayMs) {
CompletableFuture<T> result = new CompletableFuture<>();
retryAsync(task, maxAttempts, delayMs, 1, result);
return result;
}
private static <T> void retryAsync(
Supplier<CompletableFuture<T>> task,
int maxAttempts,
long delayMs,
int attempt,
CompletableFuture<T> result) {
task.get().whenComplete((response, throwable) -> {
if (throwable == null) {
result.complete(response);
} else if (attempt >= maxAttempts) {
result.completeExceptionally(throwable);
} else {
// 延迟后重试
CompletableFuture.delayedExecutor(delayMs, TimeUnit.MILLISECONDS)
.execute(() -> retryAsync(task, maxAttempts, delayMs, attempt + 1, result));
}
});
}
}
3.2 使用 Spring Retry
3.2.1 添加依赖
xml
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.0</version>
</dependency>
3.2.2 配置重试模板
java
@Configuration
@EnableRetry
public class RetryConfig {
@Bean
public RetryTemplate retryTemplate() {
RetryTemplate template = new RetryTemplate();
// 重试策略:最多重试3次,遇到特定异常时重试
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(3,
Collections.singletonMap(RuntimeException.class, true));
template.setRetryPolicy(retryPolicy);
// 退避策略:每次重试间隔1秒
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod(1000);
template.setBackOffPolicy(backOffPolicy);
return template;
}
}
3.2.3 使用 @Retryable 注解
java
@Service
public class RetryableService {
@Retryable(
value = {RuntimeException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000)
)
@Async
public CompletableFuture<String> retryableAsyncMethod() {
return CompletableFuture.supplyAsync(() -> {
// 模拟可能失败的操作
if (Math.random() < 0.7) {
throw new RuntimeException("临时错误");
}
return "成功结果";
});
}
@Recover
public CompletableFuture<String> recover(RuntimeException e) {
return CompletableFuture.completedFuture("降级结果");
}
}
3.3 高级重试策略实现
3.3.1 指数退避重试
java
public class ExponentialBackoffRetry {
public static <T> CompletableFuture<T> retryWithExponentialBackoff(
Supplier<CompletableFuture<T>> task,
int maxAttempts,
long initialDelay,
long maxDelay) {
return retryWithExponentialBackoff(task, maxAttempts, initialDelay, maxDelay, 1);
}
private static <T> CompletableFuture<T> retryWithExponentialBackoff(
Supplier<CompletableFuture<T>> task,
int maxAttempts,
long initialDelay,
long maxDelay,
int attempt) {
CompletableFuture<T> future = task.get();
if (attempt >= maxAttempts) {
return future;
}
return future.handle((result, throwable) -> {
if (throwable == null) {
return CompletableFuture.completedFuture(result);
} else {
// 计算退避时间
long delay = Math.min(maxDelay, initialDelay * (long) Math.pow(2, attempt - 1));
// 添加随机抖动避免惊群效应
delay = (long) (delay * (0.5 + Math.random()));
CompletableFuture<T> nextAttempt = CompletableFuture
.delayedExecutor(delay, TimeUnit.MILLISECONDS)
.supplyAsync(() ->
retryWithExponentialBackoff(task, maxAttempts, initialDelay, maxDelay, attempt + 1)
)
.thenCompose(cf -> cf);
return nextAttempt;
}
}).thenCompose(cf -> cf);
}
}
3.3.2 基于条件的重试
java
public class ConditionalRetry {
public static <T> CompletableFuture<T> retryWithCondition(
Supplier<CompletableFuture<T>> task,
Predicate<Throwable> shouldRetry,
int maxAttempts,
Function<Integer, Long> delayCalculator) {
CompletableFuture<T> result = new CompletableFuture<>();
retryWithCondition(task, shouldRetry, maxAttempts, delayCalculator, 1, result);
return result;
}
private static <T> void retryWithCondition(
Supplier<CompletableFuture<T>> task,
Predicate<Throwable> shouldRetry,
int maxAttempts,
Function<Integer, Long> delayCalculator,
int attempt,
CompletableFuture<T> result) {
task.get().whenComplete((response, throwable) -> {
if (throwable == null) {
result.complete(response);
return;
}
boolean canRetry = attempt < maxAttempts && shouldRetry.test(throwable);
if (!canRetry) {
result.completeExceptionally(throwable);
return;
}
long delay = delayCalculator.apply(attempt);
CompletableFuture.delayedExecutor(delay, TimeUnit.MILLISECONDS)
.execute(() ->
retryWithCondition(task, shouldRetry, maxAttempts, delayCalculator, attempt + 1, result)
);
});
}
}
3.4 完整的重试工具类
java
public class AsyncRetryUtil {
/**
* 异步重试执行器
*/
public static class AsyncRetryBuilder<T> {
private final Supplier<CompletableFuture<T>> task;
private int maxAttempts = 3;
private Predicate<Throwable> retryCondition = ex -> true;
private Function<Integer, Long> delayStrategy = attempt -> 1000L * attempt;
private Consumer<Integer> onRetry = attempt -> {};
private Function<Throwable, T> fallback = null;
private AsyncRetryBuilder(Supplier<CompletableFuture<T>> task) {
this.task = task;
}
public static <T> AsyncRetryBuilder<T> of(Supplier<CompletableFuture<T>> task) {
return new AsyncRetryBuilder<>(task);
}
public AsyncRetryBuilder<T> maxAttempts(int maxAttempts) {
this.maxAttempts = maxAttempts;
return this;
}
public AsyncRetryBuilder<T> retryIf(Predicate<Throwable> condition) {
this.retryCondition = condition;
return this;
}
public AsyncRetryBuilder<T> delayStrategy(Function<Integer, Long> strategy) {
this.delayStrategy = strategy;
return this;
}
public AsyncRetryBuilder<T> onRetry(Consumer<Integer> callback) {
this.onRetry = callback;
return this;
}
public AsyncRetryBuilder<T> fallback(Function<Throwable, T> fallback) {
this.fallback = fallback;
return this;
}
public CompletableFuture<T> execute() {
CompletableFuture<T> result = new CompletableFuture<>();
execute(1, result);
return result;
}
private void execute(int attempt, CompletableFuture<T> result) {
task.get().whenComplete((response, throwable) -> {
if (throwable == null) {
result.complete(response);
return;
}
boolean shouldRetry = attempt < maxAttempts && retryCondition.test(throwable);
if (!shouldRetry) {
if (fallback != null) {
try {
T fallbackResult = fallback.apply(throwable);
result.complete(fallbackResult);
} catch (Exception e) {
result.completeExceptionally(e);
}
} else {
result.completeExceptionally(throwable);
}
return;
}
onRetry.accept(attempt);
long delay = delayStrategy.apply(attempt);
CompletableFuture.delayedExecutor(delay, TimeUnit.MILLISECONDS)
.execute(() -> execute(attempt + 1, result));
});
}
}
}
3.5 使用示例
java
public class RetryExample {
public static void main(String[] args) throws Exception {
// 模拟不可靠的服务
Supplier<CompletableFuture<String>> unreliableService = () ->
CompletableFuture.supplyAsync(() -> {
double rand = Math.random();
if (rand < 0.8) {
throw new RuntimeException("服务暂时不可用");
}
return "服务调用成功";
});
// 使用重试工具
CompletableFuture<String> result = AsyncRetryUtil.AsyncRetryBuilder
.of(unreliableService)
.maxAttempts(5)
.retryIf(ex -> ex.getMessage().contains("暂时不可用"))
.delayStrategy(attempt -> 1000L * attempt) // 线性退避
.onRetry(attempt ->
System.out.println("第 " + attempt + " 次重试..."))
.fallback(ex -> "降级结果")
.execute();
result.whenComplete((response, error) -> {
if (error != null) {
System.out.println("最终失败: " + error.getMessage());
} else {
System.out.println("最终结果: " + response);
}
});
// 等待异步操作完成
Thread.sleep(10000);
}
}
4. 注意事项
4.1 重试策略选择
- 网络超时:使用指数退避 + 随机抖动
- 服务限流:根据返回的等待时间重试
- 业务错误:根据具体错误码决定是否重试
4.2 避免的问题
- 无限重试:设置最大重试次数
- 资源耗尽:合理控制重试频率
- 雪崩效应:使用断路器模式配合重试
- 重复操作:确保操作的幂等性
4.3 监控和日志
java
// 添加重试监控
public class RetryMonitor {
private static final MeterRegistry meterRegistry = new SimpleMeterRegistry();
public static void recordRetry(String operation, int attempt) {
Counter.builder("retry.attempts")
.tag("operation", operation)
.register(meterRegistry)
.increment();
}
public static void recordSuccess(String operation, long duration) {
Timer.builder("retry.duration")
.tag("operation", operation)
.tag("status", "success")
.register(meterRegistry)
.record(duration, TimeUnit.MILLISECONDS);
}
}
具体业务场景选择合适的重试策略,提高系统的容错能力和稳定性。
本文首发于公众号:程序员大华,专注分享前后端开发的实战笔记。关注我,少走弯路,一起进步!