增加数据库主备自动切换功能

This commit is contained in:
wanggeng 2021-11-29 14:43:35 +08:00
parent e8ebe5db61
commit 6b4e43b159
2 changed files with 204 additions and 4 deletions

View File

@ -55,12 +55,24 @@
</dependency>
<!-- cglib end -->
<!-- springboot freemarker start -->
<!-- freemarker start -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<!-- springboot freemarker end -->
<!-- freemarker end -->
<!-- durid start -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!-- durid end -->
<!-- <dependency>-->
<!-- <groupId>mysql</groupId>-->
<!-- <artifactId>mysql-connector-java</artifactId>-->
<!-- </dependency>-->
</dependencies>

View File

@ -0,0 +1,188 @@
package ink.wgink.common.datasource;
import com.alibaba.druid.filter.config.ConfigFilter;
import com.alibaba.druid.filter.encoding.EncodingConvertFilter;
import com.alibaba.druid.filter.logging.CommonsLogFilter;
import com.alibaba.druid.filter.logging.Log4j2Filter;
import com.alibaba.druid.filter.logging.Log4jFilter;
import com.alibaba.druid.filter.logging.Slf4jLogFilter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.wall.WallFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @ClassName: StandbyMySqlDataSource
* @Description: 主备mysql数据库源
* @Author: wanggeng
* @Date: 2021/11/28 9:32 下午
* @Version: 1.0
*/
@Primary
@Component
@ConfigurationProperties("spring.datasource.druid")
public class StandbyDataSource extends DruidDataSource implements InitializingBean {
private static final Logger LOG = LoggerFactory.getLogger(StandbyDataSource.class);
private ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(2);
@Autowired
private DataSourceProperties basicProperties;
private boolean lastInited;
/**
* 备库url连接
*/
private String[] standbyUrls;
private volatile int standbyIndex = 0;
@Override
public void init() throws SQLException {
lastInited = inited;
super.init();
// 第一次初始化
if (!lastInited && inited) {
// 如果没有配置不启用主备切换
if (standbyUrls == null || standbyUrls.length == 0) {
return;
}
scheduledExecutorService.schedule(new ValidateUrlTask(), 1, TimeUnit.SECONDS);
}
}
/**
* 验证连接
*/
private class ValidateUrlTask implements Runnable {
@Override
public void run() {
// 备库下标
while (true) {
// 如果这个数据源被关闭了,就结束这个定时任务
if (isClosed()) {
LOG.debug("Jdbc connection closed");
break;
}
//如果这个数据源已经被初始化了,同时连接异常才进行处理
if (isInited() && !isConnectionActive()) {
// 主库有问题时切换备库
LOG.debug("Change standby urls, index:{}", standbyIndex);
String standbyUrl = standbyUrls[standbyIndex];
changeUrl(standbyUrl);
standbyIndex++;
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
LOG.error(e.getMessage(), e);
}
}
}
}
/**
* 切换连接
*
* @param url
*/
private void changeUrl(String url) {
LOG.debug("jdbc url: {}", url);
inited = false;
setUrl(url);
}
public void setStandbyUrls(String[] standbyUrls) {
this.standbyUrls = standbyUrls;
}
/**
* 判断连接是否存活
*
* @return
*/
public boolean isConnectionActive() {
try (Connection conn = getConnection();
Statement stmt = conn.createStatement();
) {
stmt.execute("select 1");
return true;
} catch (SQLException e) {
LOG.error(e.getMessage());
return false;
}
}
public void afterPropertiesSet() throws Exception {
if (super.getUsername() == null) {
super.setUsername(this.basicProperties.determineUsername());
}
if (super.getPassword() == null) {
super.setPassword(this.basicProperties.determinePassword());
}
if (super.getUrl() == null) {
super.setUrl(this.basicProperties.determineUrl());
}
if (super.getDriverClassName() == null) {
super.setDriverClassName(this.basicProperties.getDriverClassName());
}
}
@Autowired(required = false)
public void addStatFilter(StatFilter statFilter) {
super.filters.add(statFilter);
}
@Autowired(required = false)
public void addConfigFilter(ConfigFilter configFilter) {
super.filters.add(configFilter);
}
@Autowired(required = false)
public void addEncodingConvertFilter(EncodingConvertFilter encodingConvertFilter) {
super.filters.add(encodingConvertFilter);
}
@Autowired(required = false)
public void addSlf4jLogFilter(Slf4jLogFilter slf4jLogFilter) {
super.filters.add(slf4jLogFilter);
}
@Autowired(required = false)
public void addLog4jFilter(Log4jFilter log4jFilter) {
super.filters.add(log4jFilter);
}
@Autowired(required = false)
public void addLog4j2Filter(Log4j2Filter log4j2Filter) {
super.filters.add(log4j2Filter);
}
@Autowired(required = false)
public void addCommonsLogFilter(CommonsLogFilter commonsLogFilter) {
super.filters.add(commonsLogFilter);
}
@Autowired(required = false)
public void addWallFilter(WallFilter wallFilter) {
super.filters.add(wallFilter);
}
}