Google HttpResponse使用重试逻辑时如何更新请求头
解决方案
核心结论
原生HttpBackOffUnsuccessfulResponseHandler的handleResponse方法被final修饰,无法继承重写,因此无法基于原生实现扩展请求头修改逻辑。推荐优先选择自定义响应处理器的方案,比手动捕获异常更易复用,也能完美匹配你的需求。
方案1:自定义退避响应处理器(推荐)
直接实现HttpUnsuccessfulResponseHandler接口,复用你现有的退避规则和BackOffRequired实现,在重试前新增nonce更新逻辑,同时支持读取API返回的重试等待时长覆盖退避时间:
public class NonceRefreshBackoffResponseHandler implements HttpUnsuccessfulResponseHandler { private final BackOff backOff; private final BackOffRequired backOffRequired; private final Sleeper sleeper = Sleeper.DEFAULT; // 传入nonce生成逻辑,解耦实现 private final Supplier<String> nonceSupplier; public NonceRefreshBackoffResponseHandler(BackOff backOff, BackOffRequired backOffRequired, Supplier<String> nonceSupplier) { this.backOff = backOff; this.backOffRequired = backOffRequired; this.nonceSupplier = nonceSupplier; } @Override public boolean handleResponse(HttpRequest request, HttpResponse response, boolean supportsRetry) throws IOException { if (!supportsRetry || !backOffRequired.isRequired(response)) { return false; } // 优先读取接口返回的重试等待时长,单位通常为秒,可根据实际接口规则调整 String retryAfter = response.getHeaders().getFirstHeaderStringValue("Retry-After"); long waitTime; if (retryAfter != null) { waitTime = Long.parseLong(retryAfter) * 1000; } else { waitTime = backOff.nextBackOffMillis(); } if (waitTime == BackOff.STOP) { return false; } try { sleeper.sleep(waitTime); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } // 刷新nonce,更新请求头 String newNonce = nonceSupplier.get(); // 重新构造payload或者直接更新header里的nonce,根据你的实际逻辑调整 HttpHeaders headers = request.getHeaders(); String oldPayload = headers.getFirstHeaderStringValue("payload"); // 示例:替换payload中的旧nonce字段,可根据你的payload结构调整实现 String newPayload = oldPayload.replaceAll("\"nonce\":\"[^\"]+\"", "\"nonce\":\"" + newNonce + "\""); headers.set("payload", newPayload); request.setHeaders(headers); return true; } }
改造后的调用代码:
// 请注意以下请求头仅为示例,数据不具备真实性 HttpHeaders headers = new HttpHeaders() .setContentType("text/plain").setContentLength(0L) .set("header-1", header1Data.toString()) .set("payload", payload.toString()); // 包含初始nonce HttpResponse resp = httpRequestFactory.buildPostRequest(url, null) .setHeaders(headers) .setConnectTimeout(30000) .setReadTimeout(30000) .setUnsuccessfulResponseHandler( // 传入退避策略、自定义BackOffRequired、nonce生成逻辑 new NonceRefreshBackoffResponseHandler( new ExponentialBackoff(), BlanketBackoffRequirement.SERVER_ERROR_OR_RATE_LIMIT, // 这里写你的nonce生成逻辑,每次重试会调用一次生成新的nonce () -> UUID.randomUUID().toString() ) ) .execute();
方案2:手动捕获异常重试(适合单一场景)
如果只有这一处接口调用需要该逻辑,不需要复用,可以直接用循环实现,逻辑更直观:
int maxRetry = 3; int retryCount = 0; HttpResponse resp = null; while (retryCount < maxRetry) { try { // 每次重试重新构造请求,生成新的nonce String newNonce = UUID.randomUUID().toString(); // 重新生成包含新nonce的payload Payload newPayload = buildPayloadWithNonce(newNonce); HttpHeaders headers = new HttpHeaders() .setContentType("text/plain").setContentLength(0L) .set("header-1", header1Data.toString()) .set("payload", newPayload.toString()); resp = httpRequestFactory.buildPostRequest(url, null) .setHeaders(headers) .setConnectTimeout(30000) .setReadTimeout(30000) .execute(); // 请求成功跳出循环 break; } catch (HttpResponseException e) { // 判断是否符合重试条件 if (BlanketBackoffRequirement.SERVER_ERROR_OR_RATE_LIMIT.isRequired(e.getResponse()) && retryCount < maxRetry - 1) { // 读取接口返回的等待时长 String retryAfter = e.getResponse().getHeaders().getFirstHeaderStringValue("Retry-After"); long waitTime = retryAfter != null ? Long.parseLong(retryAfter) * 1000 : new ExponentialBackoff().nextBackOffMillis(); Thread.sleep(waitTime); retryCount++; continue; } // 不符合重试条件直接抛出异常 throw e; } }
选型建议
- 若多处接口调用都需要退避+nonce刷新的逻辑,选方案1,复用性更高
- 若仅单处调用,选方案2,无需额外实现类,逻辑更直观,排查问题更简单
内容的提问来源于stack exchange,提问作者K.Niemczyk
相关产品推荐
相关产品推荐

