From 6b4e43b1598d54c4e0bb436ed4b3878cf912b09b Mon Sep 17 00:00:00 2001 From: wanggeng <450292408@qq.com> Date: Mon, 29 Nov 2021 14:43:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E4=B8=BB=E5=A4=87=E8=87=AA=E5=8A=A8=E5=88=87=E6=8D=A2=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/pom.xml | 20 +- .../common/datasource/StandbyDataSource.java | 188 ++++++++++++++++++ 2 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 common/src/main/java/ink/wgink/common/datasource/StandbyDataSource.java diff --git a/common/pom.xml b/common/pom.xml index 26fe3f9c..7d1703ac 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -55,12 +55,24 @@ - + - org.springframework.boot - spring-boot-starter-freemarker + org.freemarker + freemarker - + + + + + com.alibaba + druid + + + + + + + diff --git a/common/src/main/java/ink/wgink/common/datasource/StandbyDataSource.java b/common/src/main/java/ink/wgink/common/datasource/StandbyDataSource.java new file mode 100644 index 00000000..f8e06116 --- /dev/null +++ b/common/src/main/java/ink/wgink/common/datasource/StandbyDataSource.java @@ -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); + } + +}