Java案例如何实现服务重试?

wen python案例 1

本文目录导读:

Java案例如何实现服务重试?

  1. 手动循环重试(最简单)
  2. 使用Spring Retry(推荐)
  3. 使用Resilience4j(功能更全面)
  4. 使用Guava Retrying(轻量级)
  5. 关键设计原则

手动循环重试(最简单)

public class SimpleRetryDemo {
    public String callRemoteService() {
        int maxRetries = 3;
        int retryDelay = 1000; // 毫秒
        for (int i = 0; i < maxRetries; i++) {
            try {
                // 调用远程服务
                return doRemoteCall();
            } catch (Exception e) {
                if (i == maxRetries - 1) {
                    throw new RuntimeException("服务调用失败,已重试" + maxRetries + "次", e);
                }
                try {
                    Thread.sleep(retryDelay);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("重试被中断", ex);
                }
            }
        }
        throw new RuntimeException("未知错误");
    }
    private String doRemoteCall() {
        // 模拟远程调用
        return "success";
    }
}

缺点: 代码侵入性强,不易维护,不支持灵活配置


使用Spring Retry(推荐)

添加依赖

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

启用重试

@Configuration
@EnableRetry
public class RetryConfig {
}

使用注解方式

@Service
public class OrderService {
    @Retryable(
        value = {RemoteAccessException.class, TimeoutException.class},
        maxAttempts = 3,
        backoff = @Backoff(delay = 1000, multiplier = 2) // 指数退避
    )
    public String createOrder(Order order) {
        // 调用远程订单服务
        return remoteOrderService.create(order);
    }
    @Recover
    public String recover(RemoteAccessException e, Order order) {
        // 所有重试失败后的兜底处理
        log.error("创建订单失败,进行降级处理", e);
        return "fallback_order_id";
    }
}

编程式重试

@Service
public class RetryTemplateService {
    @Autowired
    private RetryTemplate retryTemplate;
    public String callWithRetry() {
        return retryTemplate.execute(context -> {
            // 重试次数检查
            if (context.getRetryCount() > 0) {
                log.info("第{}次重试", context.getRetryCount());
            }
            return remoteService.call();
        }, context -> {
            // 重试耗尽后的恢复逻辑
            log.error("重试耗尽,执行降级");
            return "fallback";
        });
    }
}

使用Resilience4j(功能更全面)

添加依赖

<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-retry</artifactId>
</dependency>

配置重试

@Configuration
public class Resilience4jConfig {
    @Bean
    public Retry retry() {
        RetryConfig config = RetryConfig.custom()
            .maxAttempts(3)
            .waitDuration(Duration.ofMillis(1000))
            .retryExceptions(IOException.class, TimeoutException.class)
            .ignoreExceptions(BusinessException.class) // 业务异常不重试
            .build();
        RetryRegistry registry = RetryRegistry.of(config);
        return registry.retry("myService");
    }
}

使用方式

@Service
public class PaymentService {
    @Autowired
    private Retry retry;
    public String processPayment(Payment payment) {
        // 方式1: 函数式
        Supplier<String> retryableSupplier = Retry.decorateSupplier(
            retry, () -> remotePaymentService.pay(payment)
        );
        return retryableSupplier.get();
        // 方式2: 直接调用
        // return retry.executeSupplier(() -> remotePaymentService.pay(payment));
    }
}

使用Guava Retrying(轻量级)

添加依赖

<dependency>
    <groupId>com.github.rholder</groupId>
    <artifactId>guava-retrying</artifactId>
    <version>2.0.0</version>
</dependency>

实现示例

public class GuavaRetryDemo {
    public String callWithRetry() {
        Retryer<String> retryer = RetryerBuilder.<String>newBuilder()
            .retryIfExceptionOfType(IOException.class)
            .retryIfResult(result -> result == null || result.isEmpty())
            .withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECONDS))
            .withStopStrategy(StopStrategies.stopAfterAttempt(3))
            .withRetryListener(new RetryListener() {
                @Override
                public <V> void onRetry(Attempt<V> attempt) {
                    log.info("第{}次重试, 耗时:{}ms", 
                        attempt.getAttemptNumber(), 
                        attempt.getDelaySinceFirstAttempt());
                }
            })
            .build();
        try {
            return retryer.call(() -> remoteService.call());
        } catch (ExecutionException | RetryException e) {
            log.error("重试失败", e);
            return "fallback";
        }
    }
}

关键设计原则

1 重试策略

// 固定延迟
@Backoff(delay = 1000)
// 指数退避(推荐,避免雪崩)
@Backoff(delay = 1000, multiplier = 2) // 1s, 2s, 4s, 8s...
// 随机延迟(避免惊群效应)
@Backoff(delay = 1000, random = true)

2 重试条件

// 只对特定异常重试
@Retryable(value = {NetworkException.class, TimeoutException.class})
// 忽略特定异常(业务异常不重试)
@Retryable(exceptionExpression = "message.contains('timeout')")

3 幂等性设计

// 重试接口必须是幂等的
public interface IdempotentService {
    // 相同参数多次调用结果一致
    String process(String orderId, @Idempotent String token);
}

4 熔断与重试结合

@CircuitBreaker(name = "orderService", fallbackMethod = "fallback")
@Retryable(maxAttempts = 3)
public String createOrder() {
    // 先熔断保护,再重试
}

场景 推荐方案 原因
简单项目 手动循环 轻量,无需依赖
Spring项目 Spring Retry 与Spring完美集成
微服务 Resilience4j 功能全面,支持熔断限流
快速开发 Guava Retrying API简洁,灵活配置

核心建议:

  1. 必须设置最大重试次数,防止无限重试
  2. 使用指数退避策略,避免雪崩效应
  3. 只重试可恢复的异常(网络超时、服务抖动)
  4. 业务异常不要重试(参数错误、余额不足)
  5. 重试接口必须幂等,多次调用结果一致
  6. 设置超时时间,避免长时间等待
  7. 做好监控和告警,及时发现重试频繁的问题

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