SpringBoot集成Atomikos提示transaction manager not running
  • 2024-11-10 14:34:59
  • 8890 热度
  • 0 评论

Atomikos TransactionsEssentials 是一个为Java平台提供增值服务的并且开源类事务管理器,以下是包括在这个开源版本中的一些功能:

l 全面崩溃 / 重启恢复

l 兼容标准的SUN公司JTA API

l 嵌套事务

l 为XA和非XA提供内置的JDBC适配器

注释:XA:XA协议由Tuxedo首先提出的,并交给X/Open组织,作为资源管理器(数据库)与事务管理器的接口标准。Oracle、Informix、DB2和Sybase等各大数据库厂家都提供对XA的支持。XA协议采用两阶段提交方式来管理分布式事务。XA接口提供资源管理器与事务管理器之间进行通信的标准接口。XA协议包括两套函数,以xa_开头的及以ax_开头的。


使用SpringBoot集成Atomikos时出现一个问题,提示大量Warn:

15:03:01.118 [restartedMain] WARN  c.a.j.AtomikosConnectionProxy - [logWarning,24] - 
atomikos connection proxy for com.mysql.cj.jdbc.ConnectionImpl@3b8b4cb6: WARNING: transaction manager not running?
15:03:01.150 [restartedMain] WARN  c.a.j.AtomikosConnectionProxy - [logWarning,24] - 
atomikos connection proxy for com.mysql.cj.jdbc.ConnectionImpl@3b8b4cb6: WARNING: transaction manager not running?
15:03:01.151 [restartedMain] WARN  c.a.j.AtomikosConnectionProxy - [logWarning,24] - 
atomikos connection proxy for com.mysql.cj.jdbc.ConnectionImpl@3b8b4cb6: WARNING: transaction manager not running?
......


经过验证,多数据源事物正常使用,但是大量提示WARN,让我很不舒服。

它有个外国github的说法是这样的

QQ截图20210712150456.jpg

但是我感觉不可能这么多年了还有这样的问题,最终找到答案,是因为我们在配置数据源时没有显示指定分布式事务管理器

如下代码,JTA 事务配置:

import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.jta.JtaTransactionManager;
import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;

/**
 * JTA 事务配置
 */
@Configuration
public class AtomikosConfig {
	@Bean(name = "userTransaction")
	public UserTransaction userTransaction() throws Throwable {
		UserTransactionImp userTransactionImp = new UserTransactionImp();
		userTransactionImp.setTransactionTimeout(10000);
		return userTransactionImp;
	}

	@Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
	public TransactionManager atomikosTransactionManager() throws Throwable {
		UserTransactionManager userTransactionManager = new UserTransactionManager();
		userTransactionManager.setForceShutdown(false);
		return userTransactionManager;
	}

	@Bean(name = "transactionManager")
	@DependsOn({ "userTransaction", "atomikosTransactionManager" })
	public PlatformTransactionManager transactionManager() throws Throwable {
		UserTransaction userTransaction = userTransaction();
		TransactionManager atomikosTransactionManager = atomikosTransactionManager();
		return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
	}
}

因为使用的是Druid,配置Druid多数据源:

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;

import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.ruoyi.common.enums.DataSourceType;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.config.properties.DruidProperties;
import com.ruoyi.framework.datasource.DynamicDataSource;

/**
 * druid 配置多数据源
 */
@Configuration
public class DruidConfig {
	public static final String MASTER = DataSourceType.MASTER.name();

	public static final String SLAVE = DataSourceType.SLAVE.name();

	@Autowired
	private DruidProperties druidProperties;

	@Bean
	@ConfigurationProperties("spring.datasource.druid.master")
	@DependsOn({ "transactionManager" })
	public DataSource masterDataSource(Environment env) {
		String prefix = "spring.datasource.druid.master.";
		return getDataSource(env, prefix, MASTER);
	}

	@Bean
	@ConfigurationProperties("spring.datasource.druid.slave")
	@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
	@DependsOn({ "transactionManager" })
	public DataSource slaveDataSource(Environment env) {
		String prefix = "spring.datasource.druid.slave.";
		return getDataSource(env, prefix, SLAVE);
	}

	protected DataSource getDataSource(Environment env, String prefix, String dataSourceName) {
		Properties prop = build(env, prefix);
		AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
		ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
		ds.setUniqueResourceName(dataSourceName);
		ds.setXaProperties(prop);
		return ds;
	}

	protected Properties build(Environment env, String prefix) {
		Properties prop = new Properties();
		prop.put("url", env.getProperty(prefix + "url"));
		prop.put("username", env.getProperty(prefix + "username"));
		prop.put("password", env.getProperty(prefix + "password"));
		prop.put("initialSize", druidProperties.getInitialSize());
		prop.put("minIdle", druidProperties.getMinIdle());
		prop.put("maxActive", druidProperties.getMaxActive());
		prop.put("maxWait", druidProperties.getMaxWait());
		prop.put("timeBetweenEvictionRunsMillis", druidProperties.getTimeBetweenEvictionRunsMillis());
		prop.put("minEvictableIdleTimeMillis", druidProperties.getMinEvictableIdleTimeMillis());
		prop.put("maxEvictableIdleTimeMillis", druidProperties.getMaxEvictableIdleTimeMillis());
		prop.put("validationQuery", druidProperties.getValidationQuery());
		prop.put("testWhileIdle", druidProperties.isTestWhileIdle());
		prop.put("testOnBorrow", druidProperties.isTestOnBorrow());
		prop.put("testOnReturn", druidProperties.isTestOnReturn());
		return prop;
	}

	@Bean(name = "dynamicDataSource")
	@Primary
	public DynamicDataSource dataSource(DataSource masterDataSource) {
		Map<Object, Object> targetDataSources = new HashMap<>();
		targetDataSources.put(MASTER, masterDataSource);
		setDataSource(targetDataSources, SLAVE, "slaveDataSource");
		return new DynamicDataSource(masterDataSource, targetDataSources);
	}

	/**
	 * 设置数据源
	 * 
	 * @param targetDataSources 备选数据源集合
	 * @param sourceName        数据源名称
	 * @param beanName          bean名称
	 */
	public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName) {
		try {
			DataSource dataSource = SpringUtils.getBean(beanName);
			targetDataSources.put(sourceName, dataSource);
		} catch (Exception e) {
		}
	}

	/**
	 * 去除监控页面底部的广告
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Bean
	@ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
	public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) {
		// 获取web监控页面的参数
		DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
		// 提取common.js的配置路径
		String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
		String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
		final String filePath = "support/http/resources/js/common.js";
		// 创建filter进行过滤
		Filter filter = new Filter() {
			@Override
			public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
			}

			@Override
			public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
					throws IOException, ServletException {
				chain.doFilter(request, response);
				// 重置缓冲区,响应头不会被重置
				response.resetBuffer();
				// 获取common.js
				String text = Utils.readFromResource(filePath);
				// 正则替换banner, 除去底部的广告信息
				text = text.replaceAll("<a.*?banner\"></a><br/>", "");
				text = text.replaceAll("powered.*?shrek.wang</a>", "");
				response.getWriter().write(text);
			}

			@Override
			public void destroy() {
			}
		};
		FilterRegistrationBean registrationBean = new FilterRegistrationBean();
		registrationBean.setFilter(filter);
		registrationBean.addUrlPatterns(commonJsPattern);
		return registrationBean;
	}
}


这里需要注意的是@DependsOn({ "transactionManager" })这个注解,需要给数据源显示指定为多数据源的JTA事物管理器


END


alay

Flame

Hello world!

0 评论
留下评论