csongyu/health-indicator-routing-data-source
DB Unknown Status
当 javax.sql.DataSource
接口的实现类为
org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
的子类时,调用 /actuator/health
接口返回 DB 的健康状态为
UNKNOWN。
1
| {"status":"UP","details":{"db":{"status":"UNKNOWN"},"diskSpace":{"status":"UP","details":{"total":494384795648,"free":391930544128,"threshold":10485760}}}}
|
原因在于:
org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthIndicatorAutoConfiguration
1 2 3 4 5 6 7 8 9 10 11 12
| private Map<String, DataSource> filterDataSources(Map<String, DataSource> candidates) { if (candidates == null) { return null; } Map<String, DataSource> dataSources = new LinkedHashMap<>(); candidates.forEach((name, dataSource) -> { if (!(dataSource instanceof AbstractRoutingDataSource)) { dataSources.put(name, dataSource); } }); return dataSources; }
|
org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator
1 2 3 4 5 6 7 8
| @Override protected void doHealthCheck(Health.Builder builder) throws Exception { if (this.dataSource == null) { builder.up().withDetail("database", "unknown"); } else { doDataSourceHealthCheck(builder); } }
|
自定义 Health Indicator
通过 org.springframework.jdbc.core.JdbcTemplate
执行
SELECT 1 FROM DUAL
语句检查 DB 的健康状态。
xyz.csongyu.healthindicator.RoutingDataSourceHealthIndicator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @PostConstruct @SuppressWarnings("unchecked") public void init() throws NoSuchFieldException, IllegalAccessException { final Field field = AbstractRoutingDataSource.class.getDeclaredField("resolvedDataSources"); field.setAccessible(true); final Map<Object, DataSource> resolvedDataSources = (Map<Object, DataSource>)field.get(this.dataSource); this.jdbcTemplates = resolvedDataSources.entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, entry -> new JdbcTemplate(entry.getValue()))); }
@Override public Health health() { final List<Health> results = this.jdbcTemplates.entrySet().stream().map(entry -> { final JdbcTemplate jdbcTemplate = entry.getValue(); try { jdbcTemplate.queryForObject("SELECT 1 FROM DUAL", String.class); return Health.up().withDetail("dataSource", entry.getKey()).build(); } catch (final DataAccessException e) { return Health.down().withDetail("dataSource", entry.getKey()).withException(e).build(); } }).collect(Collectors.toList());
... }
|
1
| {"status":"UP","details":{"routingDataSource":{"status":"UP","details":{"DATA_SOURCE_B":"UP","DATA_SOURCE_A":"UP"}},"diskSpace":{"status":"UP","details":{"total":494384795648,"free":391919927296,"threshold":10485760}}}}
|