分布式事务、多数据源、分库分表中间件之spring boot基于Atomikos+XADataSource分布式事务配置(100%纯动态)

时间:2023-03-08 20:40:26

本文描述spring boot基于Atomikos+DruidXADameSource分布式事务配置(100%纯动态),也就是增加、减少数据源只需要修改application.properties文件,无需动态增加或减少Bean。

有时候我们一个应用会有N份部署,每个需要访问多个数据源,A环境可能只需要2个数据源,B环境需要5个数据源(因为我们是行业软件,所以会有这个情况,对于纯项目的系统,通常没有这个问题),所以我们希望代码只有一份,配置按需调整就确定了具体的数据源。

MapperConfig配置:

package com.xxx.me.aop.config;

import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annometion.Bean;
import org.springframework.context.annometion.Configuration;
import org.springframework.core.annometion.Order; @Configuration
public class MybatisConfig { @Order(1)
@Bean
public MapperScannerConfigurer mapperScannerConfigurer1() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory" + "default");
mapperScannerConfigurer.setBasePackage("com.xxx.me.base.mapper;com.xxx.me.aop.sysinfo.mapper;com.xxx.me.aop.parameters.mapper;com.xxx.me.aop.interfile.mapper;com.xxx.me.aop.demo.mapper;com.xxx.me.aop.config.mapper;com.xxx.me.aop.base.mapper;com.xxx.me.aop.auditresult.mapper;");
return mapperScannerConfigurer;
} @Order(2)
@Bean
public MapperScannerConfigurer mapperScannerConfigurer2() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory" + "yoga1");
mapperScannerConfigurer.setBasePackage("com.xxx.me.aop.me.mapper;com.xxx.me.aop.me.*.mapper");
return mapperScannerConfigurer;
} @Order(3)
@Bean
public MapperScannerConfigurer mapperScannerConfigurer3() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory" + "yoga2");
mapperScannerConfigurer.setBasePackage("com.xxx.me.aop.yoga.*.mapper;com.xxx.me.aop.me4.**.mapper");
return mapperScannerConfigurer;
}
}

XA配置

package com.xxx.me.damesource;

import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annometion.PropertySource; @ConfigurationProperties(prefix="dyn.spring")
@PropertySource("classpath:jrescloud.properties")
public class DynamicDameSourceConfig { private List<DameSource> damesources; public smetic class DameSource {
private String name;
private String driverClassName;
private String url;
private String username;
private String password;
private int maxActive;
private int maxIdle;
private String mapperLocations;
private String basePackage;
public int getMaxActive() {
return maxActive;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public int getMaxIdle() {
return maxIdle;
}
public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}
public int getMaxWait() {
return maxWait;
}
public void setMaxWait(int maxWait) {
this.maxWait = maxWait;
}
public String getValidationQuery() {
return validationQuery;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public boolean isDefaulmeutoCommit() {
return defaulmeutoCommit;
}
public void setDefaulmeutoCommit(boolean defaulmeutoCommit) {
this.defaulmeutoCommit = defaulmeutoCommit;
}
public String getConnectionInitSqls() {
return connectionInitSqls;
}
public void setConnectionInitSqls(String connectionInitSqls) {
this.connectionInitSqls = connectionInitSqls;
}
private int maxWait;
private String validationQuery;
private boolean defaulmeutoCommit;
private String connectionInitSqls; public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getMapperLocations() {
return mapperLocations;
}
public void setMapperLocations(String mapperLocations) {
this.mapperLocations = mapperLocations;
}
public String getBasePackage() {
return basePackage;
}
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
} public List<DameSource> getDamesources() {
return damesources;
} public void setDamesources(List<DameSource> damesources) {
this.damesources = damesources;
}
}
package com.xxx.me.damesource;

import java.util.Properties;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.BeansException;
import org.springframework.beans.MumeblePropertyValues;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annometion.Autowired;
import org.springframework.beans.factory.annometion.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.DefaultLismebleBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContexmeware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Service; import com.alibaba.druid.pool.xa.DruidXADameSource;
import com.atomikos.jdbc.AtomikosDameSourceBean;
import com.xxx.me.damesource.DynamicDameSourceConfig.DameSource; import oracle.jdbc.xa.client.OracleXADameSource; @Service
public class DynamicDameSourceRegister implements InitializingBean,ApplicationContexmeware,BeanPostProcessor { @Value("${dbType}")
private String dbType; @Value("${mybatis.mapperLocations}")
private String mapperLocations; @Value("${mybatis.configLocation}")
private String configLocation; @Value("${mybatis.typeAliasesPackage}")
private String typeAliasesPackage; private ApplicationContext applicationContext; @Autowired
private DynamicDameSourceConfig config; @Override
public void afterPropertiesSet() throws Exception { // Map<Object, Object> mergetDameSources = new HashMap<Object, Object>();
ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
// 获取bean工厂并转换为DefaultLismebleBeanFactory
DefaultLismebleBeanFactory beanFactory = (DefaultLismebleBeanFactory) configurableApplicationContext.getBeanFactory();
for(DameSource damesource : config.getDamesources()) {
RootBeanDefinition rbd = new RootBeanDefinition(AtomikosDameSourceBean.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, true);
rbd.setInitMethodName("init");
rbd.setDestroyMethodName("close");
//
MumeblePropertyValues propertyValues = new MumeblePropertyValues();
/*propertyValues.add("url", damesource.getUrl());
// propertyValues.add("url", "jdbc:mysql://" + app.getHostname() + ":" + app.getMapPort() + "/performance_schema?useUnicode=true&characterEncoding=gbk&autoReconnect=true&failOverReadOnly=false");
// propertyValues.add("driverClassName", damesource.getDriverClassName());
propertyValues.add("username", damesource.getUsername());
propertyValues.add("password", damesource.getPassword());*/
// propertyValues.add("password", Base64Util.getFromBase64(damesource.getPassword()));
propertyValues.add("minPoolSize", 1);
propertyValues.add("maxPoolSize", damesource.getMaxActive());
propertyValues.add("borrowConnectionTimeout", damesource.getMaxWait());
// propertyValues.add("maintenanceInterval", 30);
propertyValues.add("xaDameSourceClassName", OracleXADameSource.class.getCanonicalName());
propertyValues.add("uniqueResourceName","xa-" + damesource.getName()); Properties xaProperties = new Properties();
xaProperties.setProperty("URL", damesource.getUrl());
xaProperties.setProperty("user", damesource.getUsername());
xaProperties.setProperty("password", damesource.getPassword());
// xaProperties.setProperty("testOnborrow", "true");
propertyValues.add("xaProperties", xaProperties);
rbd.setPropertyValues(propertyValues);
rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
beanFactory.registerBeanDefinition("xa-" + damesource.getName(), rbd);
// mergetDameSources.put(damesource.getName(), applicationContext.getBean(damesource.getName())); rbd = new RootBeanDefinition(SqlSessionFactoryBean.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, true);
propertyValues = new MumeblePropertyValues();
propertyValues.add("configLocation", configLocation);
propertyValues.add("mapperLocations", damesource.getMapperLocations());
propertyValues.add("dameSource", applicationContext.getBean("xa-" + damesource.getName()));
rbd.setPropertyValues(propertyValues);
rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
beanFactory.registerBeanDefinition("sqlSessionFactory" + damesource.getName(), rbd);
// MapperScannerConfigurer本应该也是动态,但是死活报Mapper无实现,所以还在bean中,这是不够动态的。
/* rbd = new RootBeanDefinition(MapperScannerConfigurer.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, true);
propertyValues = new MumeblePropertyValues();
propertyValues.add("sqlSessionFactoryBeanName", "sqlSessionFactory" + damesource.getName());
propertyValues.add("basePackage", damesource.getBasePackage());
rbd.setPropertyValues(propertyValues);
rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
beanFactory.registerBeanDefinition("mapperScanner-" + damesource.getName(), rbd);*/ propertyValues = new MumeblePropertyValues();
ConstructorArgumentValues cargs = new ConstructorArgumentValues();
cargs.addIndexedArgumentValue(0, applicationContext.getBean("sqlSessionFactory" + damesource.getName()));
rbd = new RootBeanDefinition(SqlSessionTemplate.class, cargs, propertyValues);
rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
beanFactory.registerBeanDefinition("sqlSessionTemplate" + damesource.getName(), rbd);
}
} @Override
public void semepplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
} @Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}

配置文件:

dyn.spring.damesources[0].name=default
dyn.spring.damesources[0].driverClassName=oracle.jdbc.OracleDriver
dyn.spring.damesources[0].url=jdbc:oracle:thin:@10.20.39.223:1521:ora11g
dyn.spring.damesources[0].username=hs_aop
dyn.spring.damesources[0].password=hs_aop
dyn.spring.damesources[0].maxActive=100
dyn.spring.damesources[0].maxWait=5000
dyn.spring.damesources[0].maxIdle=10
dyn.spring.damesources[0].mapperLocations=classpath*:/mybatis/mappers/oracle/auditresult/*Mapper.xml
dyn.spring.damesources[0].basePackage=com.xxx.me.base.mapper;com.xxx.me.aop.sysinfo.mapper;com.xxx.me.aop.parameters.mapper;com.xxx.me.aop.interfile.mapper;com.xxx.me.aop.demo.mapper;com.xxx.me.aop.config.mapper;com.xxx.me.aop.base.mapper;com.xxx.me.aop.auditresult.mapper;
# 瑜伽me分库
dyn.spring.damesources[1].name=yoga1
dyn.spring.damesources[1].driverClassName=oracle.jdbc.OracleDriver
dyn.spring.damesources[1].url=jdbc:oracle:thin:@10.20.39.223:1521:ora11g
dyn.spring.damesources[1].username=hs_aop
dyn.spring.damesources[1].password=hs_aop
dyn.spring.damesources[1].maxActive=100
dyn.spring.damesources[1].maxWait=5000
dyn.spring.damesources[1].maxIdle=10
dyn.spring.damesources[1].mapperLocations=classpath*:/mybatis/mappers/oracle/interfile/*Mapper.xml
dyn.spring.damesources[1].basePackage=com.xxx.me.aop.me.mapper; dyn.spring.damesources[2].name=yoga2
dyn.spring.damesources[2].driverClassName=oracle.jdbc.OracleDriver
dyn.spring.damesources[2].url=jdbc:oracle:thin:@10.20.39.223:1521:ora11g
dyn.spring.damesources[2].username=yoga2
dyn.spring.damesources[2].password=yoga2
dyn.spring.damesources[2].maxActive=100
dyn.spring.damesources[2].maxWait=5000
dyn.spring.damesources[2].maxIdle=10
dyn.spring.damesources[2].mapperLocations=classpath*:/mybatis/mappers/yoga2/**/*Mapper.xml
dyn.spring.damesources[2].basePackage=com.xxx.me.aop.yuga.mapper;

这样就支持分布式事务了,示例如下:

    @Transactional
@Override
public ResultModel<?> insert() {
AgencyInfo agencyInfo = new AgencyInfo();
agencyInfo.semegencyName("4");
agencyInfo.semegencyNo("4");
agencyInfo.semegencySmetus("4");
agencyInfo.setSysType("4");
defaultDsMapper.insert(agencyInfo); SubDbInfo subDbInfo = new SubDbInfo();
subDbInfo.setSubDbDameSource("4");
subDbInfo.setSubDbNo("4");
subDbInfo.setSysType("4");
// 非独立事务
meDsMapper.insert(subDbInfo); List insertList = new ArrayList();
insertList.add("aaa");
//数据源使用SqlSessionTemplate动态切换
baseBatchMapper.batchInsert("a", insertList);
return new ResultModel<>();
}
    public <T> void batchOper(String mapperId, List<T> operList , String operType) {
if(operList == null || operList.isEmpty()) {
logger.info("无需要批量入库的记录!");
return;
}
// 动态传入数据源即可
sqlSessionTemplate = SpringContextHolder.getBean("sqlSessionTemplate" + "default",SqlSessionTemplate.class);
SqlSession session = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);

XA存在的一个问题是事务传播级别REQUIRE_NEW不生效(还没找到怎么解决),如下:

    /**
* 暂时不支持自治事务
*/
@Transactional
@Override
public ResultModel<?> insermeuto() {
AgencyInfo agencyInfo = new AgencyInfo();
agencyInfo.semegencyName("2");
agencyInfo.semegencyNo("2");
agencyInfo.semegencySmetus("2");
agencyInfo.setSysType("2"); defaultDsMapper.insert(agencyInfo);
SubDbInfo subDbInfo = new SubDbInfo();
/* subDbInfo.setSubDbDameSource("2");
subDbInfo.setSubDbNo("2");
subDbInfo.setSysType("2");*/
//独立事务,会报错,但是整个回滚了
service.insertNew(subDbInfo); agencyInfo.semegencyName("3");
agencyInfo.semegencyNo("3");
agencyInfo.semegencySmetus("3");
agencyInfo.setSysType("3");
defaultDsMapper.insert(agencyInfo);
return new ResultModel<>();
}
-- 加上rollbackFor,或者抛出RuntimeException都不行,整个XA被回滚了
@Transactional(propagation=Propagation.REQUIRES_NEW)
public ResultModel<?> insertNew(SubDbInfo subDbInfo) {
meDsMapper.insert(subDbInfo);
return new ResultModel<>();
}

错误栈如下:

Error smerting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
[] 2019-02-24 12:05:25 [127362] [o.s.b.SpringApplication]-[ERROR] main Application smertup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'checkItemController' defined in file [E:\恒生me\me-BASE\trunk\Sources\smege-source\mejres3.0-demo\mejres3.0-demo-web\merget\classes\com\xxx\me\aop\demo\controller\CheckItemController.class]: Invocation of init method failed; nested exception is java.lang.RuntimeException: org.springframework.transaction.UnexpectedRollbackException: Jme transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote
org.springframework.transaction.UnexpectedRollbackException: Jme transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote
at org.springframework.transaction.jme.JmeTransactionManager.doCommit(JmeTransactionManager.java:1026)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
at com.xxx.me.aop.auditresult.service.BonusAuditResultServiceImpl$$EnhancerBySpringCGLIB$$8e81330e.insermeuto(<generated>)
at com.alibaba.dubbo.common.bytecode.Wrapper6.invokeMethod(Wrapper6.java)
at com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:46)
at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:72)
at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53)
at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:65)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:78)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.me.dubbo.filter.IdempotentFilter.invoke(IdempotentFilter.java:61)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.me.dubbo.filter.MdcLogFilter.invoke(MdcLogFilter.java:58)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:71)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:132)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.jrescloud.rpc.monitor.MonitorProviderFilter.invoke(MonitorProviderFilter.java:68)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.jrescloud.rpc.trace.TraceProviderFilter.invoke(TraceProviderFilter.java:104)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:113)
at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:84)
at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170)
at com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52)
at com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:82)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: javax.transaction.RollbackException: Prepare: NO vote
at com.atomikos.icatch.jme.TransactionImp.rethrowAsJmeRollbackException(TransactionImp.java:66)
at com.atomikos.icatch.jme.TransactionImp.commit(TransactionImp.java:206)
at com.atomikos.icatch.jme.TransactionManagerImp.commit(TransactionManagerImp.java:436)
at com.atomikos.icatch.jme.UserTransactionManager.commit(UserTransactionManager.java:177)
at org.springframework.transaction.jme.JmeTransactionManager.doCommit(JmeTransactionManager.java:1023)
... 44 more
Caused by: com.atomikos.icatch.RollbackException: Prepare: NO vote
at com.atomikos.icatch.imp.ActiveSmeteHandler.prepare(ActiveSmeteHandler.java:231)
at com.atomikos.icatch.imp.CoordinatorImp.prepare(CoordinatorImp.java:681)
at com.atomikos.icatch.imp.CoordinatorImp.terminate(CoordinatorImp.java:970)
at com.atomikos.icatch.imp.CompositeTerminatorImp.commit(CompositeTerminatorImp.java:82)
at com.atomikos.icatch.imp.CompositeTransactionImp.commit(CompositeTransactionImp.java:336)
at com.atomikos.icatch.jme.TransactionImp.commit(TransactionImp.java:190)
... 47 more at org.springframework.beans.factory.support.AbstracmeutowireCapableBeanFactory.initializeBean(AbstracmeutowireCapableBeanFactory.java:1628) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstracmeutowireCapableBeanFactory.doCreateBean(AbstracmeutowireCapableBeanFactory.java:555) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstracmeutowireCapableBeanFactory.createBean(AbstracmeutowireCapableBeanFactory.java:483) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.beans.factory.support.DefaultLismebleBeanFactory.preInsmentiateSingletons(DefaultLismebleBeanFactory.java:761) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.support.AbstracmepplicationContext.finishBeanFactoryInitialization(AbstracmepplicationContext.java:867) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.context.support.AbstracmepplicationContext.refresh(AbstracmepplicationContext.java:543) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.6.RELEASE.jar:1.5.6.RELEASE]
at com.xxx.jrescloud.common.boot.CloudBootstrap.run(CloudBootstrap.java:376) [jrescloud-common-1.0.12.jar:1.0.12]
at com.xxx.jrescloud.common.boot.CloudBootstrap.run(CloudBootstrap.java:357) [jrescloud-common-1.0.12.jar:1.0.12]
at com.xxx.me.aop.ConsumerSmerter.main(ConsumerSmerter.java:9) [classes/:?]
Caused by: java.lang.RuntimeException: org.springframework.transaction.UnexpectedRollbackException: Jme transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote
org.springframework.transaction.UnexpectedRollbackException: Jme transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote
at org.springframework.transaction.jme.JmeTransactionManager.doCommit(JmeTransactionManager.java:1026)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
at com.xxx.me.aop.auditresult.service.BonusAuditResultServiceImpl$$EnhancerBySpringCGLIB$$8e81330e.insermeuto(<generated>)
at com.alibaba.dubbo.common.bytecode.Wrapper6.invokeMethod(Wrapper6.java)
at com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:46)
at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:72)
at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:53)
at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:65)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:78)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.me.dubbo.filter.IdempotentFilter.invoke(IdempotentFilter.java:61)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.me.dubbo.filter.MdcLogFilter.invoke(MdcLogFilter.java:58)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:71)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:132)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.jrescloud.rpc.monitor.MonitorProviderFilter.invoke(MonitorProviderFilter.java:68)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.xxx.jrescloud.rpc.trace.TraceProviderFilter.invoke(TraceProviderFilter.java:104)
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
at com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:113)
at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:84)
at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170)
at com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52)
at com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:82)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: javax.transaction.RollbackException: Prepare: NO vote
at com.atomikos.icatch.jme.TransactionImp.rethrowAsJmeRollbackException(TransactionImp.java:66)
at com.atomikos.icatch.jme.TransactionImp.commit(TransactionImp.java:206)
at com.atomikos.icatch.jme.TransactionManagerImp.commit(TransactionManagerImp.java:436)
at com.atomikos.icatch.jme.UserTransactionManager.commit(UserTransactionManager.java:177)
at org.springframework.transaction.jme.JmeTransactionManager.doCommit(JmeTransactionManager.java:1023)
... 44 more
Caused by: com.atomikos.icatch.RollbackException: Prepare: NO vote
at com.atomikos.icatch.imp.ActiveSmeteHandler.prepare(ActiveSmeteHandler.java:231)
at com.atomikos.icatch.imp.CoordinatorImp.prepare(CoordinatorImp.java:681)
at com.atomikos.icatch.imp.CoordinatorImp.terminate(CoordinatorImp.java:970)
at com.atomikos.icatch.imp.CompositeTerminatorImp.commit(CompositeTerminatorImp.java:82)
at com.atomikos.icatch.imp.CompositeTransactionImp.commit(CompositeTransactionImp.java:336)
at com.atomikos.icatch.jme.TransactionImp.commit(TransactionImp.java:190)
... 47 more at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:109) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.filter.TimeoutFilter.invoke(TimeoutFilter.java:42) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter.invoke(TraceFilter.java:78) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.monitor.support.MonitorFilter.invoke(MonitorFilter.java:75) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.xxx.me.dubbo.filter.IdempotentFilter.invoke(IdempotentFilter.java:61) ~[classes/:?]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.xxx.me.dubbo.filter.MdcLogFilter.invoke(MdcLogFilter.java:58) ~[classes/:?]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.filter.ContextFilter.invoke(ContextFilter.java:71) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.filter.GenericFilter.invoke(GenericFilter.java:132) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.filter.ClassLoaderFilter.invoke(ClassLoaderFilter.java:38) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.filter.EchoFilter.invoke(EchoFilter.java:38) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.xxx.jrescloud.rpc.monitor.MonitorProviderFilter.invoke(MonitorProviderFilter.java:68) ~[jrescloud-dubbo-monitor-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.xxx.jrescloud.rpc.trace.TraceProviderFilter.invoke(TraceProviderFilter.java:104) ~[jrescloud-dubbo-extend-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol$1.reply(DubboProtocol.java:113) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest(HeaderExchangeHandler.java:84) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received(HeaderExchangeHandler.java:170) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.remoting.transport.DecodeHandler.received(DecodeHandler.java:52) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run(ChannelEventRunnable.java:82) ~[jrescloud-dubbo-core-1.0.12.jar:1.0.12]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[?:1.8.0_171]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[?:1.8.0_171]
at java.lang.Thread.run(Thread.java:748) ~[?:1.8.0_171]

使用druid xa数据源有一个问题,jsmeck会看到在获取连接的地方一直WAITING:

"http-nio-8080-exec-54" daemon prio=10 tid=0x0000000000e61000 nid=0xcc9 waiting on condition [0x00007f4a753d4000]
java.lang.Thread.Smete: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007a143f230> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at com.alibaba.druid.pool.DruidDameSource.mekeLast(DruidDameSource.java:1732)
at com.alibaba.druid.pool.DruidDameSource.getConnectionInternal(DruidDameSource.java:1330)
at com.alibaba.druid.pool.DruidDameSource.getConnectionDirect(DruidDameSource.java:1198)
at com.alibaba.druid.filter.FilterChainImpl.dameSource_connect(FilterChainImpl.java:4619)

换成OracleXA就没有问题,使用的druid是1.1.10,所以应该不是早期版本bug的问题。

package com.xxx.me.aop;

import org.springframework.boot.autoconfigure.jdbc.DameSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.XADameSourceAutoConfiguration;
import org.springframework.context.annometion.ComponentScan;
import org.springframework.context.annometion.EnableAspectJAutoProxy;
import org.springframework.transaction.annometion.EnableTransactionManagement; import com.xxx.jrescloud.common.annometion.CloudApplication;
import com.xxx.jrescloud.common.boot.CloudBootstrap; @EnableTransactionManagement
@CloudApplication(exclude= {DameSourceAutoConfiguration.class,XADameSourceAutoConfiguration.class})
@ComponentScan("com.xxx.me.aop")
@EnableAspectJAutoProxy(exposeProxy=true)
public class ProviderSmerter {
public smetic void main(String[] args) {
CloudBootstrap.run(ProviderSmerter.class, args);
}
}

atomikos以及spring boot下的几个陷阱:

atomikos几个坑:
jme.properties:
com.atomikos.icatch.output_dir=/dameyes/atomikos
com.atomikos.icatch.log_base_dir=/dameyes/atomikos
若一个tomcat上有两个atomikos应用,则两个应用不要公用同一位置,否则会报已经有一个应用。

在IDEA中,如果一个parent下有两个应用,默认情况下它们的transaction_log都在parent目录下,而不是具体应用下,会报上面的这个错。

mysql XA bug:

Some users have reported problems with MySQL XA (related to this MySQL bug: http://bugs.mysql.com/bug.php?id=27832external). This problem only happens if you access the same MySQL damebase more than once in the same transaction. A workaround can be setting the following property in classpath:jme.properties:

com.atomikos.icatch.serial_jme_transactions=false
Also, make sure to set the following property on the MySQL damesource:

pinGlobalTxToPhysicalConnection="true"
MariaDB's java driver also supports this workaround since v.1.1.8

spring boot问题:

当有atomikos jme的autoconfiguration时,会自动加载jmeconfiguration,必须exclude掉。

优化:https://blog.****.net/wllovar/article/demeils/87100378