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

Spring Boot 2.3.2项目Mongo Socket异常自动重试配置与本地测试

针对你遇到的Spring Boot MongoDB操作重试问题,结合你使用的2.3.2.RELEASE版本,我给你梳理下解决方案和测试方法:

一、先搞清楚默认的重试逻辑

首先要明确两个关键点:

  • MongoDB驱动的retryWrites=true参数:它只针对已建立连接后出现的特定可重试写入操作(比如幂等的插入、更新、删除),而且只处理驱动层面定义的少数异常,像你测试时用的「无效URI/连接被拒绝」这种连接初始化失败的情况,它是不处理的。
  • Spring Data MongoDB默认不自动重试所有Mongo相关异常:你遇到的MongoSocketWriteException、MongoSocketReadException、MongoSocketOpenException这些,Spring Data本身没有内置的全局重试机制,需要手动配置。

二、启用Spring层面的自动重试

要覆盖这些异常的重试,需要用到Spring的spring-retry模块,结合AOP拦截Mongo操作的异常,具体步骤如下:

1. 添加依赖

在你的Gradle构建文件中加入两个依赖:

implementation 'org.springframework.retry:spring-retry'
implementation 'org.springframework:spring-aspects'

2. 开启重试功能

在Spring Boot主类上添加@EnableRetry注解,开启重试支持:

@SpringBootApplication
@EnableRetry
public class YourApplication {
    public static void main(String[] args) {
        SpringApplication.run(YourApplication.class, args);
    }
}

3. 配置重试规则

你可以选择两种方式配置:

方式一:针对单个Repository方法

在你的MongoRepository接口的方法上,添加@Retryable注解,指定要重试的异常、次数和间隔:

@Repository
public interface YourEntityRepository extends MongoRepository<YourEntity, String> {

    @Retryable(
            value = {MongoReadException.class, MongoWriteException.class, 
                    MongoSocketWriteException.class, MongoSocketReadException.class,
                    MongoSocketOpenException.class, MongoTimeoutException.class},
            maxAttempts = 3, // 最多重试3次
            backoff = @Backoff(delay = 1000, multiplier = 2) // 第一次延迟1秒,之后每次间隔翻倍
    )
    @Override
    <S extends YourEntity> S save(S entity);
}

方式二:全局配置(针对所有Mongo操作)

如果想对所有MongoTemplate的操作统一重试,可以自定义RetryTemplate并注入到MongoTemplate中:

@Configuration
public class MongoRetryConfig {

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

        // 重试策略:最多3次
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(3);

        // 指定需要重试的异常
        Map<Class<? extends Throwable>, Boolean> retryableExceptions = new HashMap<>();
        retryableExceptions.put(MongoReadException.class, true);
        retryableExceptions.put(MongoWriteException.class, true);
        retryableExceptions.put(MongoSocketWriteException.class, true);
        retryableExceptions.put(MongoSocketReadException.class, true);
        retryableExceptions.put(MongoSocketOpenException.class, true);
        retryableExceptions.put(MongoTimeoutException.class, true);
        retryPolicy.setRetryableExceptions(retryableExceptions);

        // 退避策略:每次间隔1秒
        FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
        backOffPolicy.setBackOffPeriod(1000);

        retryTemplate.setRetryPolicy(retryPolicy);
        retryTemplate.setBackOffPolicy(backOffPolicy);
        return retryTemplate;
    }

    @Bean
    public MongoTemplate mongoTemplate(MongoDbFactory mongoDbFactory, MongoConverter converter, RetryTemplate retryTemplate) {
        MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory, converter);
        mongoTemplate.setRetryTemplate(retryTemplate);
        return mongoTemplate;
    }
}

三、本地测试重试行为的正确姿势

你之前用无效URI测试的方式不对,因为这种属于连接根本没建立,驱动和Spring Retry都不会处理。正确的测试应该模拟已建立连接后出现的Socket异常:

方法1:用Docker模拟MongoDB中断

  1. 启动一个MongoDB 4.0.5容器:
docker run -d -p 27017:27017 --name test-mongo mongo:4.0.5
  1. 配置应用连接这个本地MongoDB:
spring.data.mongodb.uri=mongodb://localhost:27017/testDB?retryWrites=true
  1. 启动应用,调用save方法的同时,临时停止容器:
docker stop test-mongo
  1. 观察应用日志,应该能看到重试记录,之后再启动容器:
docker start test-mongo
  1. 查看第三次重试是否成功写入数据。

方法2:单元测试Mock异常

用Mockito模拟MongoTemplate抛出指定异常,验证重试次数:

@SpringBootTest
@EnableRetry
public class RetryTest {

    @Autowired
    private YourService yourService;

    @MockBean
    private MongoTemplate mongoTemplate;

    @Test
    public void testSaveRetry() {
        YourEntity entity = new YourEntity();
        // 模拟前两次抛异常,第三次成功
        doThrow(MongoSocketWriteException.class)
                .doThrow(MongoSocketWriteException.class)
                .doNothing()
                .when(mongoTemplate).save(entity);

        yourService.saveEntity(entity);

        // 验证save方法被调用了3次
        verify(mongoTemplate, times(3)).save(entity);
    }
}

四、补充说明

  • MongoDB驱动的retryWrites=true和Spring Retry是互补的:前者负责驱动层的细粒度写入重试,后者负责业务层的全局异常重试,建议同时开启。
  • 重试时要确保操作是幂等的:比如用insertOne时如果重试可能会插入重复数据,建议用updateOne配合upsert=true这类幂等操作。

内容的提问来源于stack exchange,提问作者Sravani Ch

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:23:46