Java案例怎么实现接口重试?

wen java案例 14

本文目录导读:

Java案例怎么实现接口重试?

  1. 简单循环重试(手动实现)
  2. 使用Spring Retry
  3. 使用Guava Retrying
  4. 使用Apache HttpClient的重试机制
  5. 使用线程池的异步重试
  6. 自定义注解方式(AOP实现)
  7. 最佳实践建议

在Java中实现接口重试有多种方式,从简单的手动重试到使用成熟的第三方库,以下是几种常见实现方案:

简单循环重试(手动实现)

public class SimpleRetryDemo {
    public static void main(String[] args) {
        SimpleRetryDemo demo = new SimpleRetryDemo();
        String result = demo.retry(() -> {
            // 模拟远程调用
            return demo.callExternalService();
        }, 3, 1000);
        System.out.println("最终结果: " + result);
    }
    /**
     * 通用重试方法
     * @param callable 要执行的操作
     * @param maxRetries 最大重试次数
     * @param retryDelay 重试间隔(毫秒)
     */
    public <T> T retry(Callable<T> callable, int maxRetries, long retryDelay) {
        int retries = 0;
        while (true) {
            try {
                return callable.call();
            } catch (Exception e) {
                retries++;
                if (retries >= maxRetries) {
                    throw new RuntimeException("重试次数耗尽", e);
                }
                System.out.println("第" + retries + "次重试...");
                try {
                    Thread.sleep(retryDelay);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("重试被中断", ie);
                }
            }
        }
    }
    // 模拟外部服务调用
    private String callExternalService() {
        if (Math.random() > 0.3) {
            throw new RuntimeException("服务调用失败");
        }
        return "成功响应";
    }
}

使用Spring Retry

// pom.xml 添加依赖
/*
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>2.0.5</version>
</dependency>
*/
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
    // 配置重试策略
    @Retryable(
        value = {RemoteException.class, TimeoutException.class},  // 指定重试的异常类型
        maxAttempts = 3,  // 最大重试次数
        backoff = @Backoff(delay = 1000, multiplier = 2)  // 退避策略
    )
    public String createOrder(Order order) {
        // 调用远程服务
        return remoteService.createOrder(order);
    }
    // 所有重试失败后的降级方法
    @Recover
    public String recover(RemoteException e, Order order) {
        // 日志记录
        System.out.println("所有重试失败,执行降级逻辑");
        return "降级结果";
    }
}

使用Guava Retrying

// pom.xml 添加依赖
/*
<dependency>
    <groupId>com.github.rholder</groupId>
    <artifactId>guava-retrying</artifactId>
    <version>2.0.0</version>
</dependency>
*/
import com.github.rholder.retry.*;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
public class GuavaRetryDemo {
    public static void main(String[] args) {
        GuavaRetryDemo demo = new GuavaRetryDemo();
        demo.retryWithGuava();
    }
    public void retryWithGuava() {
        Callable<String> callable = () -> {
            // 调用外部接口
            if (Math.random() > 0.5) {
                throw new RuntimeException("接口调用失败");
            }
            return "成功";
        };
        Retryer<String> retryer = RetryerBuilder.<String>newBuilder()
            .retryIfExceptionOfType(RuntimeException.class)  // 仅重试指定异常
            .retryIfResult(result -> result.equals("失败"))    // 根据结果重试
            .withWaitStrategy(WaitStrategies.exponentialWait(100, 5000, TimeUnit.MILLISECONDS))  // 指数退避
            .withStopStrategy(StopStrategies.stopAfterAttempt(5))  // 最多重试5次
            .withRetryListener(new RetryListener() {  // 重试监听器
                @Override
                public <V> void onRetry(Attempt<V> attempt) {
                    System.out.println("第" + attempt.getAttemptNumber() + "次尝试");
                    if (attempt.hasException()) {
                        System.out.println("异常: " + attempt.getExceptionCause().getMessage());
                    }
                }
            })
            .build();
        try {
            String result = retryer.call(callable);
            System.out.println("最终结果: " + result);
        } catch (Exception e) {
            System.out.println("重试全部失败: " + e.getMessage());
        }
    }
}

使用Apache HttpClient的重试机制

import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
public class HttpClientRetryDemo {
    public static void main(String[] args) {
        // 自定义重试处理器
        DefaultHttpRequestRetryHandler retryHandler = 
            new DefaultHttpRequestRetryHandler(3, true) {  // 最多重试3次
                @Override
                public boolean retryRequest(IOException exception, 
                                           int executionCount, 
                                           HttpContext context) {
                    // 自定义重试逻辑
                    if (exception instanceof ConnectTimeoutException) {
                        // 连接超时重试
                        return executionCount < 5;
                    }
                    if (exception instanceof SocketTimeoutException) {
                        // 读取超时不重试
                        return false;
                    }
                    return super.retryRequest(exception, executionCount, context);
                }
            };
        CloseableHttpClient httpClient = HttpClients.custom()
            .setRetryHandler(retryHandler)
            .setDefaultRequestConfig(RequestConfig.custom()
                .setConnectTimeout(5000)
                .setSocketTimeout(5000)
                .build())
            .build();
        // 使用httpClient发送请求...
    }
}

使用线程池的异步重试

import java.util.concurrent.*;
import java.util.function.Supplier;
public class AsyncRetryDemo {
    private final ExecutorService executor = Executors.newFixedThreadPool(5);
    public static void main(String[] args) {
        AsyncRetryDemo demo = new AsyncRetryDemo();
        CompletableFuture<String> future = demo.asyncRetry(
            () -> "result",  // 业务逻辑
            3, 
            1000
        );
        future.thenAccept(System.out::println)
              .exceptionally(e -> {
                  System.out.println("最终失败: " + e.getMessage());
                  return null;
              });
        // 等待异步任务完成
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public <T> CompletableFuture<T> asyncRetry(
            Supplier<T> supplier, 
            int maxRetries, 
            long retryDelay) {
        CompletableFuture<T> future = new CompletableFuture<>();
        executor.submit(() -> {
            int retries = 0;
            while (retries < maxRetries) {
                try {
                    T result = supplier.get();
                    future.complete(result);
                    return;
                } catch (Exception e) {
                    retries++;
                    System.out.println("第" + retries + "次重试");
                    if (retries >= maxRetries) {
                        future.completeExceptionally(e);
                        return;
                    }
                    try {
                        TimeUnit.MILLISECONDS.sleep(retryDelay);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        future.completeExceptionally(ie);
                        return;
                    }
                }
            }
        });
        return future;
    }
    public void shutdown() {
        executor.shutdown();
    }
}

自定义注解方式(AOP实现)

import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Retryable {
    int maxRetries() default 3;
    long retryDelay() default 1000;
    Class<? extends Throwable>[] value() default {};
}
// 使用AOP实现重试逻辑
@Aspect
@Component
public class RetryAspect {
    @Around("@annotation(retryable)")
    public Object retry(ProceedingJoinPoint joinPoint, Retryable retryable) throws Throwable {
        int maxRetries = retryable.maxRetries();
        long retryDelay = retryable.retryDelay();
        Class<? extends Throwable>[] retryExceptions = retryable.value();
        int attempts = 0;
        while (true) {
            try {
                return joinPoint.proceed();
            } catch (Throwable e) {
                attempts++;
                if (attempts >= maxRetries) {
                    throw e;  // 重试次数耗尽,抛出异常
                }
                // 检查是否应该重试
                if (retryExceptions.length > 0 && !shouldRetry(e, retryExceptions)) {
                    throw e;
                }
                System.out.println("第" + attempts + "次重试...");
                Thread.sleep(retryDelay);
            }
        }
    }
    private boolean shouldRetry(Throwable e, Class<? extends Throwable>[] retryExceptions) {
        for (Class<? extends Throwable> clazz : retryExceptions) {
            if (clazz.isInstance(e)) {
                return true;
            }
        }
        return false;
    }
}

最佳实践建议

  1. 选择合适的重试策略

    • 网络相关的错误(超时、连接失败)适合重试
    • 业务逻辑错误(参数错误、权限不足)不应重试
    • 幂等性操作适合重试
  2. 退避策略

    • 固定延迟:适用于简单的重试场景
    • 指数退避:避免雪崩效应
    • 随机延迟:防止惊群效应
  3. 监控和日志

    • 记录每次重试的详细信息
    • 设置重试次数的监控指标
    • 超过阈值时发送告警
  4. 重试次数限制

    • 建议3-5次,避免过多重试
    • 考虑业务SLA要求

选择哪种方案取决于项目的具体需求和技术栈,对于简单的项目,使用手动重试就足够了;对于复杂的微服务架构,推荐使用Spring Retry或Guava Retrying等成熟的库。

抱歉,评论功能暂时关闭!