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