英文:
Shutown Spring when DB is lost
问题
使用案例
我们的 spring-boot-backend(版本 2.3.1)使用 postgres 数据库,并使用 HikariCP 作为连接池。
应用程序正在运行,管理员意外关闭了数据库。
期望的行为
Spring 注意到与数据库的连接丢失。应用程序将会优雅地关闭。
实际行为
Spring 仍然在运行。所有需要数据库的方法最终都会出现异常。
为了恢复到完全良好的状态,我们需要手动重新启动 Spring。
备注
我们有许多异步的工作程序,当数据库恢复正常时,它们无法正确恢复。
而且 Docker/Kubernetes 会注意到应用程序的关闭,并可以自动重新启动它。
问题
我如何实现期望的行为?
遗憾的是,我在网上找不到类似的解决方案。
英文:
Use case
Our spring-boot-backend (v 2.3.1) using a postgres-database with HikariCP as connection pool.
The application is online and a admin accidentally kills the database.
Expected behavior
Spring notice that the connection to DB was lost. The application will gracefully shutdown.
Actual behavior
Spring is still running. All methods, which needs the db, ending up in exceptions.
To recover a complete good state, we need manually to restart spring.
Notes
We have a lot of async workers and they cannot recover correctly, when the database is going back online.
And docker/kubernetes will notice when the application shutdown and can automatically restart it.
Question
How can I reach the expected behavior?
Sadly I found nothing similar in the web.
答案1
得分: 3
通过 @Zubair 的提示,我构建了一个新的小型解决方案。
我正在使用 Spring-Actuator 包,因为它们有一些适用于这种用例的现成类。
我所需要的就是一个 HealthIndicator
Bean,就像这样:
@Bean
public HealthIndicator dataSourceHealthIndicator(final DataSource dataSource) {
return new DataSourceHealthIndicator(dataSource, "SELECT 1;");
}
以及一个定时的监视器(因为 HikariCP 和 HealthIndicators 都没有任何事件)。
@Scheduled(fixedRate = 10000L)
public void checkDBHealth() {
final Status status = this.dataSourceHealthIndicator.health().getStatus();
if (!Status.UP.equals(status)) {
log.error("DATABASE IS OFFLINE! SHUTTING DOWN!");
System.exit(1);
}
}
我希望这对其他人有用。
编辑
我不得不更改 HikariCP 的配置。否则,健康检查器会等待几乎无限的数据库连接。
@Bean
public DataSource dataSource() {
final HikariConfig config = new HikariConfig();
// 默认设置包括用户、密码、jdbc等...
config.setInitializationFailTimeout(1000);
config.setConnectionTimeout(1000);
config.setValidationTimeout(1000);
return new HikariDataSource(config);
}
英文:
With the hints from @Zubair I build a new small solution.
Iam using the Spring-Actuator-package, because they have some ready classes for this use-case.
All what I need was a HealthIndicator
-Bean, like this
@Bean
public HealthIndicator dataSourceHealthIndicator(final DataSource dataSource) {
return new DataSourceHealthIndicator(dataSource, "SELECT 1;");
}
and scheduled watcher (because HikariCP nor the HealthIndicators has any events).
@Scheduled(fixedRate = 10000L)
public void checkDBHealth() {
final Status status = this.dataSourceHealthIndicator.health().getStatus();
if (!Status.UP.equals(status)) {
log.error("DATABASE IS OFFLINE! SHUTTING DOWN!");
System.exit(1);
}
}
I hope that will be useful for others.
Edit
I had to change the config of HikariCP. Otherwise the health-checker waiting almost endless for database-connection.
@Bean
public DataSource dataSource() {
final HikariConfig config = new HikariConfig();
// default settings with user, password, jdbc, ...
config.setInitializationFailTimeout(1000);
config.setConnectionTimeout(1000);
config.setValidationTimeout(1000);
return new HikariDataSource(config);
}
</details>
# 答案2
**得分**: 1
如果是 Spring 2.0 版本,您可以从某个监控服务调用关闭执行器。
<details>
<summary>英文:</summary>
If its spring 2.0, you may call the shut-down actuator from some monitoring service.
</details>
# 答案3
**得分**: 1
你可以在属性文件中禁用默认提供的执行器健康指示器,并通过将自定义的 [DatasourceHealthIndicator][1] 注册为 bean 来替换它。
```java
@Bean
public DataSourceHealthIndicator dataSourceHealthIndicator(){
return new DataSourceHealthIndicator(dataSource, "SELECT 1");
}
@Component
@RequiredArgsConstructor
public class CustomHealth implements HealthIndicator {
private final DataSourceHealthIndicator healthIndicator;
@Override
public Health health() {
// ...
return Health.status(healthIndicator.health().getStatus()).build();
}
}
并且像这样设置你的属性。
application.yaml
management:
health:
db:
enabled: false
application.properties
management.health.db.enabled: false
英文:
You can disable the actuator default provided health indicators in your property files and replace your custom DatasourceHealthIndicator by register it as bean.
@Bean
public DataSourceHealthIndicator dataSourceHealthIndicator(){
return new DataSourceHealthIndicator(dataSource, "SELECT 1");
}
@Component
@RequiredArgsConstructor
public class CustomHealth implements HealthIndicator {
private final DataSourceHealthIndicator healthIndicator;
@Override
public Health health() {
// ...
return Health.status(healthIndicator.health().getStatus()).build();
}
}
And set your properties like this.
application.yaml
management:
health:
db:
enabled: false
application.properties
management.health.db.enabled: false
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论