第三方回調通知可能因網絡抖動、對方服務暫時不可用導致失敗。業務常見需求:發起重試,間隔固定時間(如 10 分鐘),最多重試 N 次(如 5 次),收到成功響應立即停止。本文提供基於 Runnable + Thread.sleep 的輕量級實現,適合低併發、無分布式協調需求的場景。
public class RequestTask implements Runnable {
private final String url;
private final String param;
private volatile String status = "false"; // volatile 保證可見性
public RequestTask(String url, String param) {
this.url = url;
this.param = param;
}
@Override
public void run() {
int maxRetries = 5;
int attempt = 1;
while (attempt <= maxRetries) {
String result = HttpRequest.sendPost(url, param); // 假設工具類同步發送
if ("ok".equalsIgnoreCase(result)) {
status = "ok";
log.info("第 {} 次重試成功,停止重試", attempt);
break;
}
log.warn("第 {} 次重試失敗,響應: {}", attempt, result);
attempt++;
if (attempt <= maxRetries) {
try {
Thread.sleep(10 * 60 * 1000L); // 10 分鐘
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢復中斷狀態
log.error("重試線程被中斷", e);
break;
}
}
}
if (!"ok".equals(status)) {
log.error("重試 {} 次均失敗,需人工介入或進入死信隊列", maxRetries);
}
}
public String getStatus() { return status; }
}
// 業務觸發點(如收到異步通知處理失敗)
RequestTask task = new RequestTask("https://api.partner.com/callback", "orderId=12345&status=paid");
new Thread(task, "Callback-Retry-Order12345").start();
// 異步執行,主線程不阻塞。可通過 task.getStatus() 或異步回調感知最終結果。
| 點 | 說明 |
|---|---|
volatile status | 跨線程可見性,主線程可輪詢或回調讀取最新狀態。 |
Thread.sleep | 阻塞當前線程,釋放 CPU。簡單但佔用線程資源,高併發下慎用。 |
| 中斷處理 | 捕獲 InterruptedException 後調用 Thread.currentThread().interrupt() 恢復中斷標誌,遵守線程中斷協議。 |
| 同步 HTTP 調用 | HttpRequest.sendPost 假設為同步阻塞調用。生產環境建議設置連接/讀取超時 (如 5s/10s)。 |
| 線程命名 | new Thread(task, "名稱") 便於日志追蹤、Arthas 排查、監控識別。 |
sleep = base * 2^attempt + random(0, jitter),避免驚群效應。sleep,支持定時調度、取消、單線程串行重試不佔用業務線程池。HttpClient (Apache) / WebClient (Spring 5+) / OkHttp 異步回調,釋放線程。本實現以 50 行核心代碼 解決固定間隔有限次數重試問題,適合單體應用、低頻回調場景。隨著業務增長,建議遷移至成熟重試框架 (Spring Retry、Resilience4j) 或引入消息隊列 (RabbitMQ 延遲隊列、Kafka 重試主題) 實現可靠、可觀測、可擴展的重試體系。