分离atomikos

This commit is contained in:
dftre 2024-10-30 13:49:18 +08:00
parent ec3b008e2b
commit d5ec8c769c
15 changed files with 198 additions and 96 deletions

View File

@ -20,7 +20,6 @@
<spring-boot.version>3.3.5</spring-boot.version> <spring-boot.version>3.3.5</spring-boot.version>
<druid.version>1.2.23</druid.version> <druid.version>1.2.23</druid.version>
<dynamic.version>3.5.2</dynamic.version> <dynamic.version>3.5.2</dynamic.version>
<transactions.version>6.0.0</transactions.version>
<jta.version>1.1</jta.version> <jta.version>1.1</jta.version>
<bitwalker.version>1.21</bitwalker.version> <bitwalker.version>1.21</bitwalker.version>
<swagger.version>3.0.0</swagger.version> <swagger.version>3.0.0</swagger.version>
@ -107,12 +106,6 @@
<version>${dynamic.version}</version> <version>${dynamic.version}</version>
</dependency> </dependency>
<!-- atomikos分布式事务 -->
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-spring-boot3-starter</artifactId>
<version>${transactions.version}</version>
</dependency>
<dependency> <dependency>
<groupId>javax.transaction</groupId> <groupId>javax.transaction</groupId>
<artifactId>jta</artifactId> <artifactId>jta</artifactId>

View File

@ -60,3 +60,8 @@ spring:
wall: wall:
config: config:
multi-statement-allow: true multi-statement-allow: true
# 是否开启分布式事务如不开启请删除atomikos插件否则atomikos相关驱动虽不生效但仍会启动
atomikos:
enabled: true

View File

@ -0,0 +1,10 @@
package com.ruoyi.common.service.datasource;
import java.util.Properties;
import javax.sql.CommonDataSource;
import javax.sql.DataSource;
public interface AfterCreateDataSource {
DataSource afterCreateDataSource(String name,Properties prop, CommonDataSource dataSource);
}

View File

@ -1,9 +1,9 @@
package com.ruoyi.common.service.datasource; package com.ruoyi.common.service.datasource;
import javax.sql.DataSource; import java.util.Properties;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import javax.sql.CommonDataSource;
public interface CreateDataSource { public interface CreateDataSource {
DataSource createDataSource(String name, DataSourceProperties dataSourceProperties); CommonDataSource createDataSource(String name, Properties prop);
} }

View File

@ -34,12 +34,6 @@
<artifactId>druid-spring-boot-3-starter</artifactId> <artifactId>druid-spring-boot-3-starter</artifactId>
</dependency> </dependency>
<!-- atomikos分布式事务 -->
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-spring-boot3-starter</artifactId>
</dependency>
<dependency> <dependency>
<groupId>javax.transaction</groupId> <groupId>javax.transaction</groupId>
<artifactId>jta</artifactId> <artifactId>jta</artifactId>

View File

@ -1,14 +1,8 @@
package com.ruoyi.framework.config; package com.ruoyi.framework.config;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -24,20 +18,6 @@ public class DynamicDataSourceProperties {
private Map<String, DataSourceProperties> datasource; private Map<String, DataSourceProperties> datasource;
private String primary; private String primary;
public void validateDataSource(DataSource dataSource) {
try (Connection conn = dataSource.getConnection()) {
String validationQuery = "SELECT 1";
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(validationQuery)) {
if (!(rs.next() && rs.getInt(1) == 1)) {
throw new RuntimeException("数据源连接验证失败:查询结果不正确");
}
}
} catch (SQLException e) {
throw new RuntimeException("数据源连接验证失败", e);
}
}
public Properties build(DataSourceProperties dataSourceProperties) { public Properties build(DataSourceProperties dataSourceProperties) {
Properties prop = new Properties(); Properties prop = new Properties();
DruidProperties druidProperties = SpringUtils.getBean(DruidProperties.class); DruidProperties druidProperties = SpringUtils.getBean(DruidProperties.class);

View File

@ -1,54 +0,0 @@
package com.ruoyi.framework.datasource;
import java.util.Properties;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import com.alibaba.druid.pool.xa.DruidXADataSource;
import com.atomikos.spring.AtomikosDataSourceBean;
import com.ruoyi.common.service.datasource.CreateDataSource;
import com.ruoyi.framework.config.AtomikosConfig;
import com.ruoyi.framework.config.DruidConfig;
import com.ruoyi.framework.config.DynamicDataSourceProperties;
@Component
@DependsOn({ "transactionManager" })
public class AtomikosDataSourceCreate implements CreateDataSource {
private static final Logger logger = LoggerFactory.getLogger(AtomikosDataSourceCreate.class);
@Autowired
private DynamicDataSourceProperties properties;
@Autowired
private DruidConfig druidConfig;
@Autowired
private AtomikosConfig atomikosConfig;
public DataSource createDataSource(String name, DataSourceProperties dataSourceProperties) {
Properties prop = properties.build(dataSourceProperties);
DruidXADataSource dataSource = new DruidXADataSource();
druidConfig.getDruidDataSources().add(dataSource);
dataSource.setConnectProperties(prop);
properties.setProperties(dataSource, prop);
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
atomikosConfig.getAtomikosDataSourceBeans().add(ds);
ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
ds.setUniqueResourceName(name);
ds.setXaDataSource(dataSource);
ds.setXaProperties(prop);
ds.setMaxPoolSize(dataSource.getMaxActive());
ds.setMinPoolSize(dataSource.getMinIdle());
properties.validateDataSource(ds);
logger.info("数据源:{} 链接成功", name);
return dataSource;
}
}

View File

@ -0,0 +1,34 @@
package com.ruoyi.framework.datasource;
import java.util.Properties;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.alibaba.druid.pool.xa.DruidXADataSource;
import com.ruoyi.common.service.datasource.CreateDataSource;
import com.ruoyi.framework.config.DruidConfig;
import com.ruoyi.framework.config.DynamicDataSourceProperties;
@Component
public class DataSourceCreate implements CreateDataSource {
private static final Logger logger = LoggerFactory.getLogger(DataSourceCreate.class);
@Autowired
private DynamicDataSourceProperties properties;
@Autowired
private DruidConfig druidConfig;
public DataSource createDataSource(String name, Properties prop) {
DruidXADataSource dataSource = new DruidXADataSource();
druidConfig.getDruidDataSources().add(dataSource);
dataSource.setConnectProperties(prop);
properties.setProperties(dataSource, prop);
return dataSource;
}
}

View File

@ -1,22 +1,32 @@
package com.ruoyi.framework.datasource; package com.ruoyi.framework.datasource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import javax.sql.CommonDataSource;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import com.ruoyi.common.service.datasource.AfterCreateDataSource;
import com.ruoyi.common.service.datasource.CreateDataSource; import com.ruoyi.common.service.datasource.CreateDataSource;
import com.ruoyi.framework.config.DynamicDataSourceProperties; import com.ruoyi.framework.config.DynamicDataSourceProperties;
@Configuration @Configuration
public class DataSourceManagement implements InitializingBean { public class DataSourceManagement implements InitializingBean {
protected final Logger logger = LoggerFactory.getLogger(DataSourceManagement.class);
private Map<String, DataSource> targetDataSources = new HashMap<>(); private Map<String, DataSource> targetDataSources = new HashMap<>();
@Autowired @Autowired
@ -25,6 +35,9 @@ public class DataSourceManagement implements InitializingBean {
@Autowired @Autowired
private CreateDataSource c; private CreateDataSource c;
@Autowired(required = false)
private AfterCreateDataSource afterCreateDataSource;
@Bean(name = "dynamicDataSource") @Bean(name = "dynamicDataSource")
@Primary @Primary
public DynamicDataSource dataSource() { public DynamicDataSource dataSource() {
@ -36,10 +49,35 @@ public class DataSourceManagement implements InitializingBean {
return new DynamicDataSource(targetDataSources.get(dataSourceProperties.getPrimary()), objectMap); return new DynamicDataSource(targetDataSources.get(dataSourceProperties.getPrimary()), objectMap);
} }
public void validateDataSource(DataSource dataSource) {
try (Connection conn = dataSource.getConnection()) {
String validationQuery = "SELECT 1";
try (Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(validationQuery)) {
if (!(rs.next() && rs.getInt(1) == 1)) {
throw new RuntimeException("数据源连接验证失败:查询结果不正确");
}
}
} catch (SQLException e) {
throw new RuntimeException("数据源连接验证失败", e);
}
}
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
dataSourceProperties.getDatasource() dataSourceProperties.getDatasource()
.forEach((name, props) -> this.putDataSource(name, c.createDataSource(name, props))); .forEach((name, props) -> {
Properties properties = dataSourceProperties.build(props);
CommonDataSource commonDataSource = c.createDataSource(name, properties);
if (afterCreateDataSource != null) {
afterCreateDataSource.afterCreateDataSource(name, properties, commonDataSource);
}
DataSource dataSource = (DataSource) commonDataSource;
logger.info("数据源:{} 校验中.......", name);
validateDataSource(dataSource);
logger.info("数据源:{} 链接成功", name);
this.putDataSource(name, dataSource);
});
} }
public DataSource getDataSource(String name) { public DataSource getDataSource(String name) {

View File

@ -2,7 +2,7 @@ package com.ruoyi.framework.datasource;
import java.util.Map; import java.util.Map;
import javax.sql.DataSource; import javax.sql.CommonDataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
@ -13,7 +13,7 @@ import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
*/ */
public class DynamicDataSource extends AbstractRoutingDataSource public class DynamicDataSource extends AbstractRoutingDataSource
{ {
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) public DynamicDataSource(CommonDataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
{ {
super.setDefaultTargetDataSource(defaultTargetDataSource); super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources); super.setTargetDataSources(targetDataSources);

View File

@ -15,6 +15,7 @@
<mybatis-plus.version>3.5.8</mybatis-plus.version> <mybatis-plus.version>3.5.8</mybatis-plus.version>
<netty.version>4.1.112.Final</netty.version> <netty.version>4.1.112.Final</netty.version>
<oss.version>3.18.1</oss.version> <oss.version>3.18.1</oss.version>
<transactions.version>6.0.0</transactions.version>
</properties> </properties>
<description> <description>
@ -95,6 +96,13 @@
<version>${ruoyi.version}</version> <version>${ruoyi.version}</version>
</dependency> </dependency>
<!-- atomikos-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-atomikos</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- oss--> <!-- oss-->
<dependency> <dependency>
<groupId>com.aliyun.oss</groupId> <groupId>com.aliyun.oss</groupId>
@ -102,6 +110,13 @@
<version>${oss.version}</version> <version>${oss.version}</version>
</dependency> </dependency>
<!-- atomikos分布式事务 -->
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-spring-boot3-starter</artifactId>
<version>${transactions.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
@ -115,6 +130,7 @@
<module>ruoyi-mybatis-interceptor</module> <module>ruoyi-mybatis-interceptor</module>
<module>ruoyi-netty</module> <module>ruoyi-netty</module>
<module>ruoyi-alibaba-oss</module> <module>ruoyi-alibaba-oss</module>
<module>ruoyi-atomikos</module>
</modules> </modules>
<packaging>pom</packaging> <packaging>pom</packaging>
</project> </project>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ruoyi-plugins</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.8.8.3.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-atomikos</artifactId>
<description>
ruoyi-atomikos
</description>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId>
</dependency>
<!-- atomikos分布式事务 -->
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-spring-boot3-starter</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,8 +1,9 @@
package com.ruoyi.framework.config; package com.ruoyi.atomikos.config;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.DependsOn;
@ -23,6 +24,7 @@ import jakarta.transaction.UserTransaction;
* @author ruoyi * @author ruoyi
*/ */
@Configuration @Configuration
@ConditionalOnProperty(name = "atomikos.enabled", havingValue = "true")
public class AtomikosConfig { public class AtomikosConfig {
@Bean(name = "userTransaction") @Bean(name = "userTransaction")
public UserTransaction userTransaction() throws Throwable { public UserTransaction userTransaction() throws Throwable {

View File

@ -0,0 +1,45 @@
package com.ruoyi.atomikos.datasource;
import java.util.Properties;
import javax.sql.CommonDataSource;
import javax.sql.DataSource;
import javax.sql.XADataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import com.atomikos.spring.AtomikosDataSourceBean;
import com.ruoyi.atomikos.config.AtomikosConfig;
import com.ruoyi.common.service.datasource.AfterCreateDataSource;
@Component
@ConditionalOnProperty(name = "atomikos.enabled", havingValue = "true")
@DependsOn({ "transactionManager" })
public class AtomikosDataSourceCreate implements AfterCreateDataSource {
private static final Logger logger = LoggerFactory.getLogger(AtomikosDataSourceCreate.class);
@Autowired
private AtomikosConfig atomikosConfig;
public DataSource afterCreateDataSource(String name,Properties prop, CommonDataSource dataSource) {
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
atomikosConfig.getAtomikosDataSourceBeans().add(ds);
ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
ds.setUniqueResourceName(name);
ds.setXaDataSource((XADataSource)dataSource);
ds.setXaProperties(prop);
if (prop.getProperty("minIdle") != null) {
ds.setMinPoolSize(Integer.parseInt(prop.getProperty("minIdle")));
}
if (prop.getProperty("maxActive") != null) {
ds.setMaxPoolSize(Integer.parseInt(prop.getProperty("maxActive")));
}
return ds;
}
}

View File

@ -61,6 +61,12 @@
<groupId>com.ruoyi</groupId> <groupId>com.ruoyi</groupId>
<artifactId>ruoyi-alibaba-oss</artifactId> <artifactId>ruoyi-alibaba-oss</artifactId>
</dependency> </dependency>
<!-- atomikos-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-atomikos</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>