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