spring在数据源可用之前重试连接

vxf3dgd4  于 2021-06-23  发布在  Mysql
关注(0)|答案(2)|浏览(474)

我有一个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应用程序在启动时以给定的间隔重试与数据库的连接,直到它成功地连接到数据库?

64jmpszr

64jmpszr1#

设置hikaricp initializationFailTimeout 属性设置为0(零)或负数。如本文所述:
initializationFailTimeout 此属性控制如果无法成功使用初始连接对池进行种子设定,则池是否会“快速失败”。任何正数都被认为是尝试获取初始连接的毫秒数;在此期间,应用程序线程将被阻止。如果在此超时发生之前无法获取连接,将引发异常。此超时在 connectionTimeout 句号。如果该值为零(0),hikaricp将尝试获取并验证连接。如果获得了连接,但验证失败,则会引发异常,并且池不会启动。但是,如果无法获取连接,则池将启动,但稍后获取连接的努力可能会失败。小于零的值将绕过任何初始连接尝试,并且池将在尝试在后台获取连接时立即启动。因此,以后获得连接的努力可能会失败。默认值:1

cfh9epnr

cfh9epnr2#

有另一种方法可以做到这一点,它不依赖于特定的连接池库或特定的数据库。请注意,您需要使用 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 :

@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;
    }
}

最后但并非最不重要的是,您需要通过添加 @EnableRetry spring主类注解,示例:

@EnableRetry
@SpringBootApplication
public class RetryableDbConnectionApplication {

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

}

相关问题