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

如何使用@Retryable处理HttpServerErrorException子类的重试场景?

Spring Batch 502 BadGateway异常的两种重试实现方案

问题背景

使用Spring Batch时偶尔会碰到502 HTTP响应,对应的异常日志如下:

Error finding details for ids [123, 456 ] due to I/O error on POST request for "http://localhost:18080/my/api/path/findByIds": localhost:18080 failed to respond; nested exception is org.apache.http.NoHttpResponseException: localhost:18080 failed to respond ,level:err,thread:taskExecutor-1,logClass:c.t.k.e.r.myRepository,logMethod:findPersonByIds

服务端所有错误都会被包装在HttpServerErrorException中,BadGateway是该类的子类。现在需要实现两种重试逻辑:

  • a) 仅当BadGateway异常发生时执行重试;
  • b) 出现BadGateway异常且其嵌套异常为NoHttpResponseException时执行重试。

之前直接使用@Retryable(value = NoHttpResponseException.class)未生效,原因是该异常是嵌套在BadGateway中的,Spring Retry默认只会匹配顶层异常。


场景a:仅针对BadGateway异常重试

直接在@Retryable的value参数中指定BadGateway.class即可,只要捕获到该异常就会触发重试:

import org.springframework.retry.annotation.Retryable;
import org.springframework.web.client.HttpServerErrorException.BadGateway;

@Retryable(value = BadGateway.class)
public void myMethod() {
    // 此处编写调用外部API的业务逻辑
}

注意需在项目配置类上添加@EnableRetry注解,开启Spring Retry功能。


场景b:BadGateway异常且嵌套NoHttpResponseException时重试

这种情况需要判断顶层异常为BadGateway,同时其嵌套异常链中存在NoHttpResponseException,可通过两种方式实现:

方式一:SpEL表达式简化实现

直接在@Retryable中使用exceptionExpression属性,通过SpEL直接判断嵌套异常类型,无需额外编写类:

import org.springframework.retry.annotation.Retryable;
import org.springframework.web.client.HttpServerErrorException.BadGateway;
import org.apache.http.NoHttpResponseException;

@Retryable(
    value = BadGateway.class,
    exceptionExpression = "#root.cause instanceof T(org.apache.http.NoHttpResponseException)"
)
public void myMethod() {
    // 业务逻辑代码
}

方式二:自定义异常匹配器(适配复杂逻辑)

如果需要更灵活的重试规则(比如自定义重试次数、间隔),可以编写异常匹配器结合RetryTemplate使用:

  1. 编写异常匹配器类:
import org.springframework.web.client.HttpServerErrorException;
import org.apache.http.NoHttpResponseException;

public class BadGatewayWithNoHttpResponseMatcher implements org.springframework.retry.policy.ExceptionMatcher {

    @Override
    public boolean matches(Throwable throwable) {
        // 先判断顶层异常是否为BadGateway
        if (throwable instanceof HttpServerErrorException.BadGateway) {
            // 遍历嵌套异常链,检查是否存在NoHttpResponseException
            Throwable cause = throwable.getCause();
            while (cause != null) {
                if (cause instanceof NoHttpResponseException) {
                    return true;
                }
                cause = cause.getCause();
            }
        }
        return false;
    }
}
  1. 配置自定义RetryTemplate:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.policy.ExceptionClassifierRetryPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;

@Configuration
@EnableRetry
public class RetryConfig {

    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();

        // 基于异常分类策略,根据匹配结果决定重试规则
        ExceptionClassifierRetryPolicy classifierPolicy = new ExceptionClassifierRetryPolicy();
        classifierPolicy.setExceptionClassifier(throwable -> {
            BadGatewayWithNoHttpResponseMatcher matcher = new BadGatewayWithNoHttpResponseMatcher();
            if (matcher.matches(throwable)) {
                // 匹配成功时,最多重试3次
                SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
                retryPolicy.setMaxAttempts(3);
                return retryPolicy;
            } else {
                // 不匹配则不重试
                return new SimpleRetryPolicy(0);
            }
        });

        retryTemplate.setRetryPolicy(classifierPolicy);
        return retryTemplate;
    }
}
  1. 在业务方法中使用RetryTemplate执行逻辑:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    @Autowired
    private RetryTemplate retryTemplate;

    public void myMethod() {
        retryTemplate.execute(context -> {
            // 此处编写调用外部API的业务逻辑
            return null;
        });
    }
}

内容的提问来源于stack exchange,提问作者Chaitanya Pandit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 02:15:43