Spring重试连接,直到数据源可用。

23

我有一个docker-compose设置,用于启动我的SpringBoot应用程序和MySQL数据库。如果首先启动数据库,则我的应用程序可以成功连接。但是,如果我的应用程序首先启动,因为没有数据库存在,所以应用程序会抛出以下异常并退出:

app_1       | 2018-05-27 14:15:03.415  INFO 1 --- [           main]
com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
app_1       | 2018-05-27 14:15:06.770 ERROR 1 --- [           main]
com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Exception during pool initialization
app_1       | com.mysql.jdbc.exceptions.jdbc4.CommunicationsException:
Communications link failure

我可以编辑我的docker-compose文件,确保数据库在应用程序启动之前始终处于运行状态,但我希望应用程序能够自行处理此情况,而不是在无法访问数据库地址时立即退出。

有一些方法可以在application.properties文件中配置数据源,使应用程序重新连接到数据库,如 这里这里 所述。 但这对于启动连接到数据源并不起作用。

如何使我的SpringBoot应用程序在启动时按给定间隔重试连接到数据库,直到成功连接到数据库?

2个回答

25
将HikariCP的initializationFailTimeout属性设置为0(零)或负数。如文档所述

initializationFailTimeout

此属性控制连接池是否会在无法成功启动初始连接时“快速失败”。任何正数都表示尝试获取初始连接的毫秒数;在此期间,应用程序线程将被阻塞。如果在此超时发生之前无法获取连接,则会抛出异常。此超时应用于connectionTimeout期间之后。如果该值为零(0),HikariCP将尝试获取和验证连接。如果获取到连接但未通过验证,则会抛出异常并停止池。但是,如果无法获取连接,则池将启动,但后续获取连接的努力可能会失败。小于零的值将忽略任何初始连接尝试,并在后台启动池,同时尝试获取连接。因此,后续获取连接的努力可能会失败。默认值:1


7

有一种替代方法,不依赖于特定的连接池库或特定的数据库。请注意,您需要使用 spring-retry 来实现此方法的预期行为。

首先,您需要将 spring-retry 添加到您的依赖项中:

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>${spring-retry.version}</version>
</dependency>

然后,您可以创建一个 DataSource 装饰器, 继承 AbstractDataSource, 如下所示 :

@Slf4j
@RequiredArgsConstructor
public class RetryableDataSource extends AbstractDataSource {

    private final DataSource dataSource;

    @Override
    @Retryable(maxAttempts = 5, backoff = @Backoff(multiplier = 1.3, maxDelay = 10000))
    public Connection getConnection() throws SQLException {
        log.info("getting connection ...");
        return dataSource.getConnection();
    }

    @Override
    @Retryable(maxAttempts = 5, backoff = @Backoff(multiplier = 2.3, maxDelay = 10000))
    public Connection getConnection(String username, String password) throws SQLException {
        log.info("getting connection by username and password ...");
        return dataSource.getConnection(username, password);
    }
}

那么您需要通过创建一个自定义的 BeanPostProcessor,将这个自定义的 DataSource 装饰器注入到 Spring 上下文中:

@Slf4j
@Order(value = Ordered.HIGHEST_PRECEDENCE)
@Component
public class RetryableDatabasePostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof DataSource) {
            log.info("-----> configuring a retryable datasource for beanName = {}", beanName);
            return new RetryableDataSource((DataSource) bean);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

最后,您需要通过在Spring主类中添加@EnableRetry注解来启用Spring Retry功能,例如:
@EnableRetry
@SpringBootApplication
public class RetryableDbConnectionApplication {

    public static void main(String[] args) {
        SpringApplication.run(RetryableDbConnectionApplication.class, args);
    }

}

嘿@Abdelghani Roussi,首先感谢您的帮助。 这个方法只适用于开始时出现连接问题吗?还是适用于断断续续的连接问题? 为了提供背景,我们正在使用Azure MySQL DB,它在连接方面存在一些短暂的错误。为了缓解这种情况,考虑使用Spring Retry,这样它就不会立即失败,而是尝试连接2-3次。非常感谢您的帮助。 - Ani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接