spring cloud框架之健康检查(多数据源场景)

spring cloud框架之健康检查

引言:由于业务需要,我们框架中引入了多数据源,我们的持久层框架是spring data jpa,第一个数据源是:mysql,也是我们的主数据源,第二个数据源是:oracle,第三个数据源是sap那边的hana数据库。当引入hana数据库时,发现实例起不来了,报错误的sql语法,经过一番研究,发现是健康机制执行时,会检测每个数据源的健康程度,如果健康,则状态为:UP,如果不健康,状态为:DOWN。伴随着好奇心,我探究了下springcloud中的健康检查机制。

springcloud搭配k8s可以搭建健康检测机制,可以发现不健康的实例,并将其重启。

涉及到数据库相关你的健康检查是个配置累:DataSourceHealthIndicatorAutoConfiguration.class

@Configuration
@ConditionalOnClass({ JdbcTemplate.class, AbstractRoutingDataSource.class })
@ConditionalOnBean(DataSource.class)
@ConditionalOnEnabledHealthIndicator("db")
@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class DataSourceHealthIndicatorAutoConfiguration extends
		CompositeHealthIndicatorConfiguration<DataSourceHealthIndicator, DataSource>
		implements InitializingBean {

	private final Map<String, DataSource> dataSources;

	private final Collection<DataSourcePoolMetadataProvider> metadataProviders;

	private DataSourcePoolMetadataProvider poolMetadataProvider;

	public DataSourceHealthIndicatorAutoConfiguration(
			ObjectProvider<Map<String, DataSource>> dataSources,
			ObjectProvider<Collection<DataSourcePoolMetadataProvider>> metadataProviders) {
		this.dataSources = filterDataSources(dataSources.getIfAvailable());
		this.metadataProviders = metadataProviders.getIfAvailable();
	}

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

	@Override
	public void afterPropertiesSet() throws Exception {
		this.poolMetadataProvider = new CompositeDataSourcePoolMetadataProvider(
				this.metadataProviders);
	}

	@Bean
	@ConditionalOnMissingBean(name = "dbHealthIndicator")
	public HealthIndicator dbHealthIndicator() {
		return createHealthIndicator(this.dataSources);
	}

	@Override
	protected DataSourceHealthIndicator createHealthIndicator(DataSource source) {
		return new DataSourceHealthIndicator(source, getValidationQuery(source));
	}

	private String getValidationQuery(DataSource source) {
		DataSourcePoolMetadata poolMetadata = this.poolMetadataProvider
				.getDataSourcePoolMetadata(source);
		return (poolMetadata != null) ? poolMetadata.getValidationQuery() : null;
	}

}

这个配置类会为所有数据源初始化一个健康检查类:DataSourceHealthIndicator,未来健康检查时,会执行里面的健康检测方法,并修改健康检查的状态。 每初始化一个DataSourceHealthIndicator,都会放入上下文对象中(是一个linkedHashMap)(CompositeHealthIndicator):

private final Map<String, HealthIndicator> indicators;
public void addHealthIndicator(String name, HealthIndicator indicator) {
		this.indicators.put(name, indicator);
	}

外面一层包装(CompositeHealthIndicatorConfiguration):

protected HealthIndicator createHealthIndicator(Map<String, S> beans) {
		if (beans.size() == 1) {
			return createHealthIndicator(beans.values().iterator().next());
		}
		CompositeHealthIndicator composite = new CompositeHealthIndicator(
				this.healthAggregator);
		beans.forEach((name, source) -> composite.addHealthIndicator(name,
				createHealthIndicator(source)));
		return composite;
	}

真正在健康检查时执行的是(DataSourceHealthIndicator):

private void doDataSourceHealthCheck(Health.Builder builder) throws Exception {
		String product = getProduct();
		builder.up().withDetail("database", product);
		String validationQuery = getValidationQuery(product);
		if (StringUtils.hasText(validationQuery)) {
			// Avoid calling getObject as it breaks MySQL on Java 7
			List<Object> results = this.jdbcTemplate.query(validationQuery,
					new SingleColumnRowMapper());
			Object result = DataAccessUtils.requiredSingleResult(results);
			builder.withDetail("hello", result);
		}
	}

可以看到,一开始默认将状态设置成了UP,后面异常后会改为:DOWN。getValidationQuery(product)方法会获取要执行的sql,最后用jdbcTemplate来验证数据库的连通性。

看到这里,其实一开始遇到的问题就有答案了,这里获取到的验证sql为:select 1,而hana数据库是根本不支持这种语法的,另外,如果验证sql为空的话,也会默认执行:/ping/ select 1。

这个验证sql加载的地方是在(DataSourceHealthIndicatorAutoConfiguration):

private String getValidationQuery(DataSource source) {
		DataSourcePoolMetadata poolMetadata = this.poolMetadataProvider
				.getDataSourcePoolMetadata(source);
		return (poolMetadata != null) ? poolMetadata.getValidationQuery() : null;
	}

这里会取配置中的connectionTestQuery。一般在mysql中测试sql为:select 1;在oracle中测试sql为:select Hello from dual。而在hana数据库中,可以将一张空表作为测试表来构造测试sql。

这里是设置connectionTestQuery的其中一个方法:

public HikariDataSource dataSource() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setMinimumIdle(0);
        dataSource.setIdleTimeout(10000);
        dataSource.setConnectionTestQuery("select count(1) from test.\"student\"");
        return dataSource;
    }
    @Bean
    public DataSourcePoolMetadataProvider hikariPoolDataSourceMetadataProvider(HikariDataSource hikariDataSource){
        return dataSource -> new HikariDataSourcePoolMetadata((HikariDataSource) dataSource);
    }

这样的话,实例就可以起起来了。

看起来好像没什么问题了,但是当其他数据源挂掉后,我们的实例由于健康检查不通过,导致实例异常,表现是:本系统与异常数据源无关的功能也用不了。于是,应该健康检查时,只检查主数据源即可,其他数据源在用的时候知道是否健康即可。

这里有一种方案,可以在初始化DataSourceHealthIndicator的时候,只初始化主数据源相关的Indicator:

@Bean
    public HealthIndicator dbHealthIndicator(HikariDataSource dataSource, HealthAggregator healthAggregator) {
        CompositeHealthIndicator composite = new CompositeHealthIndicator(
                healthAggregator);
        composite.addHealthIndicator("dataSource", new DataSourceHealthIndicator(dataSource, null));
        return composite;
    }

这样的话,当健康检查数据源时,只会检查主数据源mysql,这里将测试sql设置成了null,意在用默认的select 1。

修改前:当其他数据库挂了的时候,我们正在运行的服务器实例也变得异常了,原有的功能也用不了了。
修改后:当其他数据库挂了的时候,我们的服务器实例正常运行,原油的功能可以用。

  • 36
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud中使用多数据源可以通过配置多个数据源以及分别配置对应的事务管理器来实现。以下是一种常见的实现方式: 首先,在Spring Boot的配置文件中,配置多个数据源的相关信息。例如,可以使用以下配置: ```properties # 数据源1 spring.datasource.url=jdbc:mysql://localhost:3306/db1 spring.datasource.username=user1 spring.datasource.password=pass1 # 数据源2 spring.datasource.db2.url=jdbc:mysql://localhost:3306/db2 spring.datasource.db2.username=user2 spring.datasource.db2.password=pass2 ``` 接下来,在项目中创建对应的数据源配置类。可以使用`@Configuration`注解标记该类为配置类,然后使用`@Bean`注解配置多个数据源。例如: ```java @Configuration public class DataSourceConfig { @Primary @Bean(name = "dataSource1") @ConfigurationProperties(prefix = "spring.datasource") public DataSource dataSource1() { return DataSourceBuilder.create().build(); } @Bean(name = "dataSource2") @ConfigurationProperties(prefix = "spring.datasource.db2") public DataSource dataSource2() { return DataSourceBuilder.create().build(); } } ``` 在上述配置类中,使用了`@Primary`注解来标记数据源1为主要数据源,这样在注入数据源时会优先选择数据源1。 接着,在需要使用数据源的地方,可以使用`@Qualifier`注解指定具体的数据源。例如,可以在Repository层的代码中使用`@Qualifier`注解指定具体的数据源: ```java @Repository public class UserRepository { private final JdbcTemplate jdbcTemplate; @Autowired public UserRepository(@Qualifier("dataSource1") DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } // ... } ``` 以上就是使用Spring Cloud实现多数据源的一种方式。通过配置多个数据源以及使用`@Qualifier`注解指定具体的数据源,可以实现在不同的业务场景下使用不同的数据源。 相关问题: 1. 如何在事务管理中使用多数据源? 2. 是否可以动态切换数据源? 3. 多数据源会带来哪些挑战和注意事项?

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值