← 返回首页
🔍

重试退避

📂 architecture ⏱ 2 min 206 words

重试退避

重试策略概览

重试机制应对瞬时故障,通过指数退避避免重试风暴,配合幂等设计保证多次调用结果一致。

指数退避示例:
第1次重试: 1s
第2次重试: 2s
第3次重试: 4s
第4次重试: 8s
第5次重试: 16s(达到最大延迟)

指数退避实现

指数退避将重试间隔指数级增长,避免大量重试同时涌入导致系统雪崩。

public class ExponentialBackoff {
    private final long initialDelay;
    private final long maxDelay;
    private final double multiplier;

    public ExponentialBackoff(long initialDelay, long maxDelay, double multiplier) {
        this.initialDelay = initialDelay;
        this.maxDelay = maxDelay;
        this.multiplier = multiplier;
    }

    public <T> T executeWithRetry(Supplier<T> action) {
        long delay = initialDelay;
        int attempt = 0;
        while (true) {
            try {
                return action.get();
            } catch (Exception e) {
                attempt++;
                if (attempt >= 5) throw new RuntimeException("Max retries exceeded", e);
                sleep(delay + ThreadLocalRandom.current().nextLong(delay / 2));
                delay = Math.min((long) (delay * multiplier), maxDelay);
            }
        }
    }
}

幂等设计

幂等保证多次相同请求产生相同结果,是重试机制的前提条件。

@RestController
public class OrderController {
    @PostMapping("/orders")
    public Order createOrder(@RequestHeader("Idempotency-Key") String key,
                            @RequestBody OrderRequest request) {
        // 幂等键检查
        if (idempotencyStore.exists(key)) {
            return idempotencyStore.get(key);
        }
        
        Order order = orderService.create(request);
        idempotencyStore.save(key, order, Duration.ofHours(24));
        return order;
    }
}

重试与退避配置

// Spring Retry配置
@Configuration
@EnableRetry
public class RetryConfig {
    @Bean
    public RetryTemplate retryTemplate() {
        return RetryTemplate.builder()
            .maxAttempts(3)
            .exponentialBackoff(1000, 2, 10000)
            .retryOn(HttpServerErrorException.class)
            .build();
    }
}

// 使用示例
@Retryable(
    value = {HttpServerErrorException.class},
    maxAttempts = 3,
    backoff = @Backoff(delay = 1000, multiplier = 2)
)
public User getUser(String id) {
    return restTemplate.getForObject("http://user-service/users/" + id, User.class);
}

重试注意事项

要点 说明
幂等前提 非幂等操作不可重试
超时设置 重试总时间不超过业务超时
退避策略 指数退避+随机抖动
降级兜底 重试耗尽后触发降级