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

View File

@ -60,3 +60,8 @@ spring:
wall:
config:
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;
import javax.sql.DataSource;
import java.util.Properties;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import javax.sql.CommonDataSource;
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>
</dependency>
<!-- atomikos分布式事务 -->
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-spring-boot3-starter</artifactId>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>jta</artifactId>

View File

@ -1,14 +1,8 @@
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.Properties;
import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@ -24,20 +18,6 @@ public class DynamicDataSourceProperties {
private Map<String, DataSourceProperties> datasource;
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) {
Properties prop = new Properties();
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;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.sql.CommonDataSource;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.ruoyi.common.service.datasource.AfterCreateDataSource;
import com.ruoyi.common.service.datasource.CreateDataSource;
import com.ruoyi.framework.config.DynamicDataSourceProperties;
@Configuration
public class DataSourceManagement implements InitializingBean {
protected final Logger logger = LoggerFactory.getLogger(DataSourceManagement.class);
private Map<String, DataSource> targetDataSources = new HashMap<>();
@Autowired
@ -25,6 +35,9 @@ public class DataSourceManagement implements InitializingBean {
@Autowired
private CreateDataSource c;
@Autowired(required = false)
private AfterCreateDataSource afterCreateDataSource;
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource() {
@ -36,10 +49,35 @@ public class DataSourceManagement implements InitializingBean {
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
public void afterPropertiesSet() throws Exception {
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) {

View File

@ -2,7 +2,7 @@ package com.ruoyi.framework.datasource;
import java.util.Map;
import javax.sql.DataSource;
import javax.sql.CommonDataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
@ -13,7 +13,7 @@ import org.springframework.jdbc.datasource.lookup.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.setTargetDataSources(targetDataSources);

View File

@ -15,6 +15,7 @@
<mybatis-plus.version>3.5.8</mybatis-plus.version>
<netty.version>4.1.112.Final</netty.version>
<oss.version>3.18.1</oss.version>
<transactions.version>6.0.0</transactions.version>
</properties>
<description>
@ -76,7 +77,7 @@
<version>${ruoyi.version}</version>
</dependency>
<dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-netty</artifactId>
<version>${ruoyi.version}</version>
@ -95,6 +96,13 @@
<version>${ruoyi.version}</version>
</dependency>
<!-- atomikos-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-atomikos</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- oss-->
<dependency>
<groupId>com.aliyun.oss</groupId>
@ -102,6 +110,13 @@
<version>${oss.version}</version>
</dependency>
<!-- atomikos分布式事务 -->
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-spring-boot3-starter</artifactId>
<version>${transactions.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
@ -115,6 +130,7 @@
<module>ruoyi-mybatis-interceptor</module>
<module>ruoyi-netty</module>
<module>ruoyi-alibaba-oss</module>
<module>ruoyi-atomikos</module>
</modules>
<packaging>pom</packaging>
</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.List;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
@ -23,6 +24,7 @@ import jakarta.transaction.UserTransaction;
* @author ruoyi
*/
@Configuration
@ConditionalOnProperty(name = "atomikos.enabled", havingValue = "true")
public class AtomikosConfig {
@Bean(name = "userTransaction")
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

@ -55,12 +55,18 @@
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-netty</artifactId>
</dependency>
<!--oss配置-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-alibaba-oss</artifactId>
</dependency>
<!-- atomikos-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-atomikos</artifactId>
</dependency>
</dependencies>
</project>