You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 21:06:03