guava-retrying重试工具库: AttemptTimeLimiter

本文介绍如何利用Guava库中的TimeLimiter组件来限制Java中Callable任务的最大执行时间,以防止单个任务占用过多资源。同时展示了如何结合Retryer进行重试策略配置,确保每次重试不会超过指定时间。

AttemptTimeLimiter和guava的TimeLimiter基本是一样的,是为了限制某个任务的执行时间。比如我希望一个Callable任务执行时间不超过5s,如果超过抛异常TimeoutException。

import com.google.common.util.concurrent.SimpleTimeLimiter;
import com.google.common.util.concurrent.TimeLimiter;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

public class TestTimeLimiter {

    public static void main(String[] args) throws Exception {
        TimeLimiter timeLimiter = new SimpleTimeLimiter();

        timeLimiter.callWithTimeout(buildTask(), 5, TimeUnit.SECONDS, false);
    }

    private static Callable<Boolean> buildTask() {
        return new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                long start = System.currentTimeMillis();
                long end = start;
                while (end - start <= 10 * 1000) {
                    end = System.currentTimeMillis();
                }

                return true;
            }
        };
    }
}



AttemptTimeLimiter功能与之类似,保证某次重试的执行时间不超过特定值。

public class TestLimit {

    private static SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss,SSS");

    public static void main(String[] args) throws Exception {

        Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
                .retryIfException()
                .retryIfResult(Predicates.equalTo(false))
                .withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(1, TimeUnit.SECONDS))
                .withStopStrategy(StopStrategies.stopAfterAttempt(5))
                .build();

        System.out.println("begin..." + df.format(new Date()));

        try {
            retryer.call(buildTask());
        } catch (Exception e) {
            System.err.println("still failed after retry." + e.getCause().toString());
        }

        System.out.println("end..." + df.format(new Date()));

    }

    private static Callable<Boolean> buildTask() {
        return new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                System.out.println("called");
                long start = System.currentTimeMillis();
                long end = start;
                while (end - start <= 10 * 1000) {
                    end = System.currentTimeMillis();
                }

                return true;
            }
        };
    }

}



<think>我们使用Guava Retrying库来实现重试机制。首先需要添加依赖,然后编写重试逻辑。 注意:Guava Retrying不是Google Guava核心库的一部分,而是一个基于Guava的扩展库。 步骤: 1. 添加Maven依赖 2. 定义重试策略(重试条件、等待策略、停止策略等) 3. 使用Retryer执行需要重试的方法 示例代码:</think>### 使用Guava实现重试机制 Guava Retrying库提供了灵活的重试策略实现,以下是完整实现方案: #### 1. 添加Maven依赖 ```xml <dependency> <groupId>com.github.rholder</groupId> <artifactId>guava-retrying</artifactId> <version>2.0.0</version> </dependency> ``` #### 2. 基础重试实现 ```java import com.github.rholder.retry.*; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; public class GuavaRetryDemo { public static void main(String[] args) { // 定义重试策略 Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder() .retryIfException() // 出现异常重试 .retryIfResult(result -> result == false) // 返回false重试 .withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 最多重试3次 .withWaitStrategy(WaitStrategies.fixedWait(500, TimeUnit.MILLISECONDS)) // 固定500ms间隔 .withRetryListener(new MyRetryListener()) // 重试监听器 .build(); try { // 执行带重试的方法 Boolean result = retryer.call(new RemoteServiceCall()); System.out.println("最终结果: " + result); } catch (ExecutionException | RetryException e) { System.err.println("重试全部失败: " + e.getMessage()); } } // 模拟远程服务调用 static class RemoteServiceCall implements Callable<Boolean> { private int attempt = 0; @Override public Boolean call() throws Exception { attempt++; System.out.println("执行第" + attempt + "次调用..."); // 模拟业务逻辑 if (attempt < 3) { throw new ServiceException("服务暂时不可用"); } return true; } } // 自定义重试监听器 static class MyRetryListener implements RetryListener { @Override public <V> void onRetry(Attempt<V> attempt) { System.out.println("重试监听器: " + "尝试次数=" + attempt.getAttemptNumber() + ", " + "距离首次调用耗时=" + attempt.getDelaySinceFirstAttempt() + "ms"); if (attempt.hasException()) { System.out.println("失败原因: " + attempt.getExceptionCause().getMessage()); } } } // 自定义业务异常 static class ServiceException extends Exception { public ServiceException(String message) { super(message); } } } ``` #### 3. 高级配置选项 ##### 重试条件(Retry Conditions) ```java .retryIfExceptionOfType(IOException.class) // 特定异常重试 .retryIfRuntimeException() // 运行时异常重试 .retryIfResult(Predicates.equalTo(false)) // 结果等于false重试 .retryIfException(Predicates.equalTo(new ServiceException(""))) // 自定义异常判断 ``` ##### 等待策略(Wait Strategies) ```java .withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECONDS)) // 固定间隔 .withWaitStrategy(WaitStrategies.randomWait(500, 1500, TimeUnit.MILLISECONDS)) // 随机区间 .withWaitStrategy(WaitStrategies.exponentialWait(100, 5, TimeUnit.MINUTES)) // 指数退避 .withWaitStrategy(WaitStrategies.fibonacciWait(100, 2, TimeUnit.MINUTES)) // 斐波那契退避 ``` ##### 停止策略(Stop Strategies) ```java .withStopStrategy(StopStrategies.stopAfterAttempt(5)) // 按次数停止 .withStopStrategy(StopStrategies.stopAfterDelay(30, TimeUnit.SECONDS)) // 按总时长停止 ``` ##### 阻塞策略(Block Strategies) ```java .withBlockStrategy(BlockStrategers.threadSleepStrategy()) // 默认线程睡眠 .withBlockStrategy(new ThreadYieldBlockStrategy()) // 线程让步 ``` #### 4. 最佳实践建议 1. **异常处理原则** -重试可恢复异常(网络超时、临时不可用) -重试业务逻辑异常(参数错误、权限不足) - 使用`retryIfExceptionOfType()`过滤异常类型 2. **幂等性保证** ```java // 在重试逻辑中确保操作幂等性 public Boolean call() { String idempotentKey = generateIdempotentKey(); return remoteService.idempotentCall(idempotentKey, params); } ``` 3. **上下文传递** ```java // 使用AttemptTimeLimiter实现超时控制 .withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(2, TimeUnit.SECONDS)) ``` 4. **组合策略** ```java // 组合停止策略:满足任一条件即停止 .withStopStrategy(StopStrategies.stop( StopStrategies.stopAfterAttempt(5), StopStrategies.stopAfterDelay(30, TimeUnit.SECONDS) )) ``` 5. **性能监控** ```java // 在监听器中添加监控指标 @Override public <V> void onRetry(Attempt<V> attempt) { metrics.recordRetry(attempt.getAttemptNumber()); } ``` #### 5. 与Spring整合示例 ```java @Configuration public class RetryConfig { @Bean public Retryer<Boolean> apiRetryer() { return RetryerBuilder.<Boolean>newBuilder() .retryIfExceptionOfType(ServiceTimeoutException.class) .withStopStrategy(StopStrategies.stopAfterAttempt(3)) .withWaitStrategy(WaitStrategies.exponentialWait(100, 1000, TimeUnit.MILLISECONDS)) .build(); } } @Service public class ApiService { @Autowired private Retryer<Boolean> apiRetryer; public void callWithRetry() { try { apiRetryer.call(() -> { return externalApi.call(); }); } catch (Exception e) { // 处理异常 } } } ``` ### 重试设计模式的核心要点 1. **退避策略**:避免重试风暴(指数退避最佳) 2. **重试上限**:防止无限重试(建议3-5次) 3. **熔断机制**:结合Hystrix或Resilience4j实现熔断 4. **上下文隔离**:使用线程池隔离重试任务 5. **结果验证**:不仅捕获异常,还需验证返回结果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值