如何基于HttpOutboundGateway返回的400/401状态码实现请求重试
基于HTTP状态码的HttpOutboundGateway重试方案
问题根因
你当前拿到的MessagingException是Spring Integration对消息处理异常的统一外层封装,HTTP调用产生的状态码异常(如401未授权)会作为cause嵌套在异常链中,类型通常为org.springframework.web.client.HttpClientErrorException.Unauthorized,需要先解包才能获取状态码。
实现方案
可以通过自定义重试策略+异常解包的方式实现仅针对401状态码的重试,同时兼容token刷新逻辑,修改方案如下:
步骤1:自定义重试策略,仅对401异常触发重试
修改RequestHandlerRetryAdvice的配置,绑定自定义的RetryTemplate,指定只在遇到401异常时重试,同时可配置重试次数:
@Bean public RequestHandlerRetryAdvice retryAdvice() { RequestHandlerRetryAdvice retryAdvice = new RequestHandlerRetryAdvice(); // 自定义重试模板 RetryTemplate retryTemplate = new RetryTemplate(); // 自定义重试策略:仅401异常重试,最多重试2次 SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy( 2, // 最大重试次数(包含首次调用) Collections.singletonMap(HttpClientErrorException.Unauthorized.class, true), true // 匹配异常的cause链 ); retryTemplate.setRetryPolicy(retryPolicy); // 可选:配置退避策略,比如间隔1秒重试 FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy(); backOffPolicy.setBackOffPeriod(1000); retryTemplate.setBackOffPolicy(backOffPolicy); retryAdvice.setRetryTemplate(retryTemplate); // 重试耗尽后进入恢复流程 retryAdvice.setRecoveryCallback(errorMessageSendingRecoverer()); return retryAdvice; }
步骤2:恢复流中增加状态码判断逻辑
如果需要在重试耗尽后根据不同状态码做不同处理,可以在handleRecovery流程中解包异常获取HTTP状态码:
@Bean IntegrationFlow handleRecovery() { return IntegrationFlows.from("recoveryChannel") .log(Level.ERROR, "error", m -> m.getPayload()) .<MessagingException>handle((message, headers) -> { MessagingException exception = message.getPayload(); // 解包异常获取HTTP状态码 Integer httpStatus = extractHttpStatus(exception); Message<CreditRequest> originalMessage = (Message<CreditRequest>) exception.getFailedMessage(); // 仅401场景走token刷新后重试逻辑 if (httpStatus != null && httpStatus == HttpStatus.UNAUTHORIZED.value()) { // 刷新token逻辑 String newToken = gateway.getToken(configProperties); Message<CreditRequest> retryMessage = MessageBuilder.fromMessage(originalMessage) .setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + newToken) .build(); // 重试原HTTP网关,无需单独定义第二个HTTP流程 retryChannel().send(retryMessage); } else { // 其他状态码走死信队列/告警逻辑 // dlqChannel().send(originalMessage); } return null; }) .get(); } // 工具方法:从异常链中提取HTTP状态码 private Integer extractHttpStatus(Throwable ex) { Throwable current = ex; while (current != null) { if (current instanceof HttpClientErrorException || current instanceof HttpServerErrorException) { return ((HttpStatusCodeException) current).getStatusCode().value(); } current = current.getCause(); } return null; }
步骤3:优化重试流复用原有HTTP配置
不需要单独定义第二个Http.outboundGateway,直接让重试流复用你已经配置好的creditWalletHttpGateway即可,避免配置重复:
@Bean IntegrationFlow creditRequestFlow() { return IntegrationFlows.from(retryChannel()) .log(Level.INFO, "retry info", m -> m.getPayload()) .handle(creditWalletHttpGateway) // 复用原有HTTP网关配置 .get(); }
额外优化点
如果需要避免每次重试都走恢复流程,可以将token刷新逻辑放到RetryListener中,在每次重试前自动刷新token,无需单独走恢复通道,流程更简洁。
内容的提问来源于stack exchange,提问作者Johnson Eyo
相关产品推荐
相关产品推荐

