分佈式事務、多數據源、分庫分表中間件之spring boot基於Atomikos+XADataSource分佈式事務配置(100%純動態)

本文描述spring boot基於Atomikos+DruidXADataSource分佈式事務配置(100%純動態),也就是增長、減小數據源只須要修改application.properties文件,無需動態增長或減小Bean。php

有時候咱們一個應用會有N份部署,每一個須要訪問多個數據源,A環境可能只須要2個數據源,B環境須要5個數據源(由於咱們是行業軟件,因此會有這個狀況,對於純項目的系統,一般沒有這個問題),因此咱們但願代碼只有一份,配置按需調整就肯定了具體的數據源。java

 MapperConfig配置:mysql

package com.hundsun.ta.aop.config;

import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

@Configuration
public class MybatisConfig {

    @Order(1)
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer1() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory" + "default");
        mapperScannerConfigurer.setBasePackage("com.hundsun.ta.base.mapper;com.hundsun.ta.aop.sysinfo.mapper;com.hundsun.ta.aop.parameters.mapper;com.hundsun.ta.aop.interfile.mapper;com.hundsun.ta.aop.demo.mapper;com.hundsun.ta.aop.config.mapper;com.hundsun.ta.aop.base.mapper;com.hundsun.ta.aop.auditresult.mapper;");
        return mapperScannerConfigurer;
    }
    
    @Order(2)
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer2() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory" + "yoga1");
        mapperScannerConfigurer.setBasePackage("com.hundsun.ta.aop.ta.mapper;com.hundsun.ta.aop.ta.*.mapper");
        return mapperScannerConfigurer;
    }
    
    @Order(3)
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer3() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory" + "yoga2");
        mapperScannerConfigurer.setBasePackage("com.hundsun.ta.aop.yoga.*.mapper;com.hundsun.ta.aop.ta4.**.mapper");
        return mapperScannerConfigurer;
    }
}

XA配置web

package com.hundsun.ta.datasource;

import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;

@ConfigurationProperties(prefix="dyn.spring")
@PropertySource("classpath:jrescloud.properties")
public class DynamicDataSourceConfig {
    
    private List<DataSource> datasources;
    
    public static class DataSource {
        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 isDefaultAutoCommit() {
            return defaultAutoCommit;
        }
        public void setDefaultAutoCommit(boolean defaultAutoCommit) {
            this.defaultAutoCommit = defaultAutoCommit;
        }
        public String getConnectionInitSqls() {
            return connectionInitSqls;
        }
        public void setConnectionInitSqls(String connectionInitSqls) {
            this.connectionInitSqls = connectionInitSqls;
        }
        private int maxWait;
        private String validationQuery;
        private boolean defaultAutoCommit;
        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<DataSource> getDatasources() {
        return datasources;
    }

    public void setDatasources(List<DataSource> datasources) {
        this.datasources = datasources;
    }
}
package com.hundsun.ta.datasource;

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.MutablePropertyValues;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.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.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Service;

import com.alibaba.druid.pool.xa.DruidXADataSource;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.hundsun.ta.datasource.DynamicDataSourceConfig.DataSource;

import oracle.jdbc.xa.client.OracleXADataSource;

@Service
public class DynamicDataSourceRegister implements InitializingBean,ApplicationContextAware,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 DynamicDataSourceConfig config;
    
    @Override
    public void afterPropertiesSet() throws Exception {
        
//        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
        // 獲取bean工廠並轉換爲DefaultListableBeanFactory
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
        for(DataSource datasource : config.getDatasources()) {
            RootBeanDefinition rbd = new RootBeanDefinition(AtomikosDataSourceBean.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, true);
            rbd.setInitMethodName("init");
            rbd.setDestroyMethodName("close");
//            
            MutablePropertyValues propertyValues = new MutablePropertyValues();
            /*propertyValues.add("url", datasource.getUrl());
            // propertyValues.add("url", "jdbc:mysql://" + app.getHostname() + ":" + app.getMapPort() + "/performance_schema?useUnicode=true&characterEncoding=gbk&autoReconnect=true&failOverReadOnly=false");
//            propertyValues.add("driverClassName", datasource.getDriverClassName());
            propertyValues.add("username", datasource.getUsername());
            propertyValues.add("password", datasource.getPassword());*/
//            propertyValues.add("password", Base64Util.getFromBase64(datasource.getPassword()));
            propertyValues.add("minPoolSize", 1);
            propertyValues.add("maxPoolSize", datasource.getMaxActive());
            propertyValues.add("borrowConnectionTimeout", datasource.getMaxWait());
//            propertyValues.add("maintenanceInterval", 30);
            propertyValues.add("xaDataSourceClassName", OracleXADataSource.class.getCanonicalName());
            propertyValues.add("uniqueResourceName","xa-" + datasource.getName());
            
            Properties xaProperties = new Properties();
            xaProperties.setProperty("URL", datasource.getUrl());
            xaProperties.setProperty("user", datasource.getUsername());
            xaProperties.setProperty("password", datasource.getPassword());
//            xaProperties.setProperty("testOnborrow", "true");
            propertyValues.add("xaProperties", xaProperties);
            rbd.setPropertyValues(propertyValues);
            rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
            beanFactory.registerBeanDefinition("xa-" + datasource.getName(), rbd);
//            targetDataSources.put(datasource.getName(), applicationContext.getBean(datasource.getName()));
            
            rbd = new RootBeanDefinition(SqlSessionFactoryBean.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, true);
            propertyValues = new MutablePropertyValues();
            propertyValues.add("configLocation", configLocation);
            propertyValues.add("mapperLocations", datasource.getMapperLocations());
            propertyValues.add("dataSource", applicationContext.getBean("xa-" + datasource.getName()));
            rbd.setPropertyValues(propertyValues);
            rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
            beanFactory.registerBeanDefinition("sqlSessionFactory" + datasource.getName(), rbd);
            // MapperScannerConfigurer本應該也是動態,可是死活報Mapper無實現,因此還在bean中,這是不夠動態的。 /*            rbd = new RootBeanDefinition(MapperScannerConfigurer.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, true);
            propertyValues = new MutablePropertyValues();
            propertyValues.add("sqlSessionFactoryBeanName", "sqlSessionFactory" + datasource.getName());
            propertyValues.add("basePackage", datasource.getBasePackage());
            rbd.setPropertyValues(propertyValues);
            rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
            beanFactory.registerBeanDefinition("mapperScanner-" + datasource.getName(), rbd);*/
            
            propertyValues = new MutablePropertyValues();
            ConstructorArgumentValues cargs = new ConstructorArgumentValues();
            cargs.addIndexedArgumentValue(0, applicationContext.getBean("sqlSessionFactory" + datasource.getName()));
            rbd = new RootBeanDefinition(SqlSessionTemplate.class, cargs, propertyValues);
            rbd.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
            beanFactory.registerBeanDefinition("sqlSessionTemplate" + datasource.getName(), rbd);
        }
    }
    
    @Override
    public void setApplicationContext(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;
    }
}

配置文件:spring

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

dyn.spring.datasources[2].name=yoga2
dyn.spring.datasources[2].driverClassName=oracle.jdbc.OracleDriver
dyn.spring.datasources[2].url=jdbc:oracle:thin:@10.20.39.223:1521:ora11g
dyn.spring.datasources[2].username=yoga2
dyn.spring.datasources[2].password=yoga2
dyn.spring.datasources[2].maxActive=100
dyn.spring.datasources[2].maxWait=5000
dyn.spring.datasources[2].maxIdle=10
dyn.spring.datasources[2].mapperLocations=classpath*:/mybatis/mappers/yoga2/**/*Mapper.xml
dyn.spring.datasources[2].basePackage=com.hundsun.ta.aop.yuga.mapper;

這樣就支持分佈式事務了,示例以下:sql

    @Transactional
    @Override
    public ResultModel<?> insert() {
        AgencyInfo agencyInfo = new AgencyInfo();
        agencyInfo.setAgencyName("4");
        agencyInfo.setAgencyNo("4");
        agencyInfo.setAgencyStatus("4");
        agencyInfo.setSysType("4");
        defaultDsMapper.insert(agencyInfo);
        
        SubDbInfo subDbInfo = new SubDbInfo();
        subDbInfo.setSubDbDataSource("4");
        subDbInfo.setSubDbNo("4");
        subDbInfo.setSysType("4");
        // 非獨立事務
        taDsMapper.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不生效(還沒找到怎麼解決),以下tomcat

    /**
     * 暫時不支持自治事務
     */
    @Transactional
    @Override
    public ResultModel<?> insertAuto() {
        AgencyInfo agencyInfo = new AgencyInfo();
        agencyInfo.setAgencyName("2");
        agencyInfo.setAgencyNo("2");
        agencyInfo.setAgencyStatus("2");
        agencyInfo.setSysType("2");
        
        defaultDsMapper.insert(agencyInfo);
        SubDbInfo subDbInfo = new SubDbInfo();
/*        subDbInfo.setSubDbDataSource("2");
        subDbInfo.setSubDbNo("2");
        subDbInfo.setSysType("2");*/
        //獨立事務,會報錯,可是整個回滾了
        service.insertNew(subDbInfo);
        
        agencyInfo.setAgencyName("3");
        agencyInfo.setAgencyNo("3");
        agencyInfo.setAgencyStatus("3");
        agencyInfo.setSysType("3");
        defaultDsMapper.insert(agencyInfo);
        return new ResultModel<>();
    }
-- 加上rollbackFor,或者拋出RuntimeException都不行,整個XA被回滾了
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public ResultModel<?> insertNew(SubDbInfo subDbInfo) {
        taDsMapper.insert(subDbInfo);
        return new ResultModel<>();
    }

錯誤棧以下:session

Error starting 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 startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'checkItemController' defined in file [E:\恆生TA\TA-BASE\trunk\Sources\stage-source\tajres3.0-demo\tajres3.0-demo-web\target\classes\com\hundsun\ta\aop\demo\controller\CheckItemController.class]: Invocation of init method failed; nested exception is java.lang.RuntimeException: org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote
org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote
    at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.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.hundsun.ta.aop.auditresult.service.BonusAuditResultServiceImpl$$EnhancerBySpringCGLIB$$8e81330e.insertAuto(<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.hundsun.ta.dubbo.filter.IdempotentFilter.invoke(IdempotentFilter.java:61)
    at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
    at com.hundsun.ta.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.hundsun.jrescloud.rpc.monitor.MonitorProviderFilter.invoke(MonitorProviderFilter.java:68)
    at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
    at com.hundsun.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.jta.TransactionImp.rethrowAsJtaRollbackException(TransactionImp.java:66)
    at com.atomikos.icatch.jta.TransactionImp.commit(TransactionImp.java:206)
    at com.atomikos.icatch.jta.TransactionManagerImp.commit(TransactionManagerImp.java:436)
    at com.atomikos.icatch.jta.UserTransactionManager.commit(UserTransactionManager.java:177)
    at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1023)
    ... 44 more
Caused by: com.atomikos.icatch.RollbackException: Prepare: NO vote
    at com.atomikos.icatch.imp.ActiveStateHandler.prepare(ActiveStateHandler.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.jta.TransactionImp.commit(TransactionImp.java:190)
    ... 47 more

    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.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.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) ~[spring-beans-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) ~[spring-context-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.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.hundsun.jrescloud.common.boot.CloudBootstrap.run(CloudBootstrap.java:376) [jrescloud-common-1.0.12.jar:1.0.12]
    at com.hundsun.jrescloud.common.boot.CloudBootstrap.run(CloudBootstrap.java:357) [jrescloud-common-1.0.12.jar:1.0.12]
    at com.hundsun.ta.aop.ConsumerStarter.main(ConsumerStarter.java:9) [classes/:?]
Caused by: java.lang.RuntimeException: org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote
org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Prepare: NO vote
    at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.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.hundsun.ta.aop.auditresult.service.BonusAuditResultServiceImpl$$EnhancerBySpringCGLIB$$8e81330e.insertAuto(<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.hundsun.ta.dubbo.filter.IdempotentFilter.invoke(IdempotentFilter.java:61)
    at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
    at com.hundsun.ta.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.hundsun.jrescloud.rpc.monitor.MonitorProviderFilter.invoke(MonitorProviderFilter.java:68)
    at com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper$1.invoke(ProtocolFilterWrapper.java:91)
    at com.hundsun.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.jta.TransactionImp.rethrowAsJtaRollbackException(TransactionImp.java:66)
    at com.atomikos.icatch.jta.TransactionImp.commit(TransactionImp.java:206)
    at com.atomikos.icatch.jta.TransactionManagerImp.commit(TransactionManagerImp.java:436)
    at com.atomikos.icatch.jta.UserTransactionManager.commit(UserTransactionManager.java:177)
    at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:1023)
    ... 44 more
Caused by: com.atomikos.icatch.RollbackException: Prepare: NO vote
    at com.atomikos.icatch.imp.ActiveStateHandler.prepare(ActiveStateHandler.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.jta.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.hundsun.ta.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.hundsun.ta.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.hundsun.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.hundsun.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數據源有一個問題,jstack會看到在獲取鏈接的地方一直WAITING:mybatis

"http-nio-8080-exec-54" daemon prio=10 tid=0x0000000000e61000 nid=0xcc9 waiting on condition [0x00007f4a753d4000]
   java.lang.Thread.State: 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.DruidDataSource.takeLast(DruidDataSource.java:1732)
	at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1330)
	at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:1198)
	at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:4619)

換成OracleXA就沒有問題,使用的druid是1.1.10,因此應該不是早期版本bug的問題。oracle

package com.hundsun.ta.aop;


import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.hundsun.jrescloud.common.annotation.CloudApplication;
import com.hundsun.jrescloud.common.boot.CloudBootstrap;

@EnableTransactionManagement
@CloudApplication(exclude= {DataSourceAutoConfiguration.class,XADataSourceAutoConfiguration.class})
@ComponentScan("com.hundsun.ta.aop")
@EnableAspectJAutoProxy(exposeProxy=true)
public class ProviderStarter {
    public static void main(String[] args) {
        CloudBootstrap.run(ProviderStarter.class, args);
    }
}

atomikos以及spring boot下的幾個陷阱:

atomikos幾個坑:
jta.properties:
com.atomikos.icatch.output_dir=/datayes/atomikos
com.atomikos.icatch.log_base_dir=/datayes/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 database more than once in the same transaction. A workaround can be setting the following property in classpath:jta.properties:

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

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

spring boot問題:

當有atomikos jta的autoconfiguration時,會自動加載jtaconfiguration,必須exclude掉。

優化:https://blog.csdn.net/wllovar/article/details/87100378

相關文章
相關標籤/搜索