springboot+atomikos 分散式事務處理示例
一、atomikos介紹:
Atomikos TransactionsEssentials 是一個為Java平台提供增值服務的並且開源類事務管理器,以下是包括在這個開源版本中的一些功能:
- 全面崩潰 / 重啟恢復
- 兼容標準的SUN公司JTA API
- 嵌套事務
- 為XA和非XA提供內置的JDBC適配器
Atomikos TransactionsEssentials是一個非常快速的嵌入式事務管理器,這就意味著,您不需要另外啟動一個單獨的事務管理器進程(不要查找任何的bin文件夾)。Atomikos TransactionsEssentials 是一個可靠的庫,可以加入到您的Java應用程序,也就是說為了使用這個產品,您只需添加一些jar文件到您的應用程序或者應用程序伺服器。
二、pom.xml文件配置:
1、添加atomikos依賴,同時需要加上springboot jdbc的依賴:
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jta-atomikos -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
<version>${spring.boot.version}</version>
</dependency>
2、添加需要的資料庫連接池依賴,此處使用druid:
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
三、yum配置:
spring:
profiles:
active:
- test
datasource:
type: com.alibaba.druid.pool.xa.DruidXADataSource
druid:
# WebStatFilter配置,說明請參考Druid Wiki,配置_配置WebStatFilter
web-stat-filter:
enabled: true
urlPattern: /*
exclusions: /druid/*,*.js,*.gif,*.jpg,*.png,*.css,*.ico
sessionStatMaxCount:
sessionStatEnable:
principalSessionName: USER_SESSION
principalCookieName: USER_COOKIE
profileEnable: true
# StatViewServlet配置,說明請參考Druid Wiki,配置_StatViewServlet配置
stat-view-servlet:
enabled: true
urlPattern: /druid/*
resetEnable: false
loginUsername: admin
loginPassword: 123456
allow: 127.0.0.1
deny:
# Spring監控配置,說明請參考Druid Github Wiki,配置_Druid和Spring關聯監控配置
# Spring監控AOP切入點,如x.y.z.service.*,配置多個英文逗號分隔
aop-patterns:
- com.freeager.springboot.atomikos.*.service.**
database1:
name: DataSource1
url: jdbc:mysql://localhost/test
username: root
password: 123567
# 下面為連接池的補充設置,應用到上面所有數據源中
# 初始化大小,最小,最大
initialSize: 5
minIdle: 5
maxActive: 20
# 配置獲取連接等待超時的時間
maxWait: 60000
# 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一個連接在池中最小生存的時間,單位是毫秒
minEvictableIdleTimeMillis: 30
validationQuery: SELECT user()
validationQueryTimeout: 10000
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打開PSCache,並且指定每個連接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat,wall
# 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合併多個DruidDataSource的監控數據
useGlobalDataSourceStat: true
database2:
name: DataSource2
url: jdbc:mysql://localhost/mytest
username: root
password: 123567
# 下面為連接池的補充設置,應用到上面所有數據源中
# 初始化大小,最小,最大
initialSize: 5
minIdle: 5
maxActive: 20
# 配置獲取連接等待超時的時間
maxWait: 60000
# 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一個連接在池中最小生存的時間,單位是毫秒
minEvictableIdleTimeMillis: 30
validationQuery: SELECT user()
validationQueryTimeout: 10000
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打開PSCache,並且指定每個連接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat,wall
# 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合併多個DruidDataSource的監控數據
useGlobalDataSourceStat: true
#jta相關參數配置
jta:
log-dir: classpath:tx-logs
transaction-manager-id: txManager
四、配置代碼:
1、數據源:
@Configuration
public class DataSourceConfig {
@Bean(name = "DataSource1")
@Primary
@Autowired
public DataSource systemDataSource(Environment env) {
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
Properties prop = build(env, "spring.datasource.druid.database1.");
ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
ds.setUniqueResourceName("DataSource1");
ds.setPoolSize(5);
ds.setXaProperties(prop);
return ds;
}
@Autowired
@Bean(name = "DataSource2")
public DataSource businessDataSource(Environment env) {
AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
Properties prop = build(env, "spring.datasource.druid.database2.");
ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
ds.setUniqueResourceName("DataSource2");
ds.setPoolSize(5);
ds.setXaProperties(prop);
return ds;
}
@Bean("jdbcTemplate1")
public JdbcTemplate sysJdbcTemplate(@Qualifier("DataSource1") DataSource ds) {
return new JdbcTemplate(ds);
}
@Bean("jdbcTemplate2")
public JdbcTemplate busJdbcTemplate(@Qualifier("DataSource2") DataSource ds) {
return new JdbcTemplate(ds);
}
private 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("driverClassName", env.getProperty(prefix + "driverClassName", ""));
prop.put("initialSize", env.getProperty(prefix + "initialSize", Integer.class));
prop.put("maxActive", env.getProperty(prefix + "maxActive", Integer.class));
prop.put("minIdle", env.getProperty(prefix + "minIdle", Integer.class));
prop.put("maxWait", env.getProperty(prefix + "maxWait", Integer.class));
prop.put("poolPreparedStatements", env.getProperty(prefix + "poolPreparedStatements", Boolean.class));
prop.put("maxPoolPreparedStatementPerConnectionSize",
env.getProperty(prefix + "maxPoolPreparedStatementPerConnectionSize", Integer.class));
prop.put("maxPoolPreparedStatementPerConnectionSize",
env.getProperty(prefix + "maxPoolPreparedStatementPerConnectionSize", Integer.class));
prop.put("validationQuery", env.getProperty(prefix + "validationQuery"));
prop.put("validationQueryTimeout", env.getProperty(prefix + "validationQueryTimeout", Integer.class));
prop.put("testOnBorrow", env.getProperty(prefix + "testOnBorrow", Boolean.class));
prop.put("testOnReturn", env.getProperty(prefix + "testOnReturn", Boolean.class));
prop.put("testWhileIdle", env.getProperty(prefix + "testWhileIdle", Boolean.class));
prop.put("timeBetweenEvictionRunsMillis",
env.getProperty(prefix + "timeBetweenEvictionRunsMillis", Integer.class));
prop.put("minEvictableIdleTimeMillis", env.getProperty(prefix + "minEvictableIdleTimeMillis", Integer.class));
prop.put("filters", env.getProperty(prefix + "filters"));
return prop;
}
}
2、事務:
@Configuration
@ComponentScan
@EnableTransactionManagement
public class TransactionManagerConfig {
@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 UserTransactionManager atomikosTransactionManager() throws Throwable {
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setForceShutdown(false);
return userTransactionManager;
}
@Bean(name = "transactionManager")
@DependsOn({ "userTransaction", "atomikosTransactionManager" })
public JtaTransactionManager transactionManager() throws Throwable {
UserTransaction userTransaction = userTransaction();
JtaTransactionManager manager = new JtaTransactionManager(userTransaction, atomikosTransactionManager());
return manager;
}
@Bean(name = "transactionInterceptor")
public TransactionInterceptor transactionInterceptor(PlatformTransactionManager platformTransactionManager) {
TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
// 事物管理器
transactionInterceptor.setTransactionManager(platformTransactionManager);
Properties transactionAttributes = new Properties();
// test
transactionAttributes.setProperty("test*", "PROPAGATION_REQUIRED,-Throwable");
// 新增
transactionAttributes.setProperty("insert*", "PROPAGATION_REQUIRED,-Throwable");
// 修改
transactionAttributes.setProperty("update*", "PROPAGATION_REQUIRED,-Throwable");
// 刪除
transactionAttributes.setProperty("delete*", "PROPAGATION_REQUIRED,-Throwable");
// 查詢
transactionAttributes.setProperty("select*", "PROPAGATION_REQUIRED,-Throwable,readOnly");
transactionInterceptor.setTransactionAttributes(transactionAttributes);
return transactionInterceptor;
}
// 代理到ServiceImpl的Bean
@Bean
public BeanNameAutoProxyCreator transactionAutoProxy() {
BeanNameAutoProxyCreator transactionAutoProxy = new BeanNameAutoProxyCreator();
transactionAutoProxy.setProxyTargetClass(true);
transactionAutoProxy.setBeanNames("*ServiceImpl");
transactionAutoProxy.setInterceptorNames("transactionInterceptor");
return transactionAutoProxy;
}
}
3、啟動類:
@EnableAutoConfiguration
@ComponentScan
@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@Import({ DataSourceConfig.class })
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
五、測試代碼:
@Controller
@RequestMapping("/test")
public class TestController {
@Autowired
private JdbcTemplate jdbcTemplate1;
@Autowired
private JdbcTemplate jdbcTemplate2;
@Transactional
@RequestMapping("")
@ResponseBody
public String test() {
//第一個數據採用隨機數,保證第二次能執行
jdbcTemplate1.execute("insert into test_1(id) values("+new Random().nextInt(10000) +")");
//第二條插入語句第二次執行時將報錯,回滾
jdbcTemplate2.execute("insert into test_2(id) values(20)");
return "test ok...";
}
}
※windows7上使用docker容器
※使用 C 代碼實現拓撲排序
TAG:程序員小新人學習 |