Apache Seata分布式事务启用Nacos做配置中心

本文来自 Apache Seata官方文档,欢迎访问官网,查看更多深度文章。
本文来自 Apache Seata官方文档,欢迎访问官网,查看更多深度文章。
Seata分布式事务启用Nacos做配置中心

Seata分布式事务启用Nacos做配置中心

项目地址

本文作者:FUNKYE(陈健斌),杭州某互联网公司主程。

前言

上次发布了直连方式的seata配置,详细可以看这篇博客

我们接着上一篇的基础上去配置nacos做配置中心跟dubbo注册中心.

准备工作

​ 1.首先去nacos的github上下载最新版本

在这里插入图片描述

​ 2.下载好了后,很简单,解压后到bin目录下去启动就好了,看到如图所示就成了:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 3.启动完毕后访问:http://127.0.0.1:8848/nacos/#/login

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

是不是看到这样的界面了?输入nacos(账号密码相同),先进去看看吧.

这时候可以发现没有任何服务注册

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

别急我们马上让seata服务连接进来.

Seata配置

​ 1.进入seata的conf文件夹看到这个木有?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

就是它,编辑它:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 2.然后记得保存哦!接着我们把registry.conf文件打开编辑:

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    serverAddr = "localhost"
    namespace = ""
    cluster = "default"
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = "0"
  }
  zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  consul {
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
  }
  etcd3 {
    cluster = "default"
    serverAddr = "http://localhost:2379"
  }
  sofa {
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    name = "file.conf"
  }
}

config {
  # file、nacos 、apollo、zk、consul、etcd3
  type = "nacos"

  nacos {
    serverAddr = "localhost"
    namespace = ""
  }
  consul {
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    app.id = "seata-server"
    apollo.meta = "http://192.168.1.204:8801"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  }
}

都编辑好了后,我们运行nacos-config.sh,这时候我们配置的nacos-config.txt的内容已经被发送到nacos中了详细如图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

出现以上类似的代码就是说明成功了,接着我们登录nacos配置中心,查看配置列表,出现如图列表说明配置成功了:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

看到了吧,你的配置已经全部都提交上去了,如果再git工具内运行sh不行的话,试着把编辑sh文件,试试改成如下操作

for line in $(cat nacos-config.txt)

do

key=${line%%=*}
value=${line#*=}
echo "\r\n set "${key}" = "${value}

result=`curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=$key&group=SEATA_GROUP&content=$value"`

if [ "$result"x == "true"x ]; then

  echo "\033[42;37m $result \033[0m"

else

  echo "\033[41;37 $result \033[0m"
  let error++

fi

done


if [ $error -eq 0 ]; then

echo  "\r\n\033[42;37m init nacos config finished, please start seata-server. \033[0m"

else

echo  "\r\n\033[41;33m init nacos config fail. \033[0m"

fi

​ 3.目前我们的准备工作全部完成,我们去seata-service/bin去运行seata服务吧,如图所示就成功啦!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

进行调试

​ 1.首先把springboot-dubbo-mybatsiplus-seata项目的pom的依赖更改,去除掉zk这些配置,因为我们使用nacos做注册中心了.

	<properties>
		<webVersion>3.1</webVersion>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
		<HikariCP.version>3.2.0</HikariCP.version>
		<mybatis-plus-boot-starter.version>3.2.0</mybatis-plus-boot-starter.version>
	</properties>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.8.RELEASE</version>
	</parent>
	<dependencies>
		<dependency>
			<groupId>com.alibaba.nacos</groupId>
			<artifactId>nacos-client</artifactId>
			<version>1.1.4</version>
		</dependency>
		<dependency>
			<groupId>org.apache.dubbo</groupId>
			<artifactId>dubbo-registry-nacos</artifactId>
			<version>2.7.4.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.dubbo</groupId>
			<artifactId>dubbo-spring-boot-starter</artifactId>
			<version>2.7.4.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.60</version>
		</dependency>
		<!-- <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> 
			<version>7.0</version> <scope>provided</scope> </dependency> -->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.9.2</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.9.2</version>
		</dependency>
 
		<!-- mybatis-plus begin -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>${mybatis-plus-boot-starter.version}</version>
		</dependency>
		<!-- mybatis-plus end -->
		<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>io.seata</groupId>
			<artifactId>seata-all</artifactId>
			<version>0.9.0.1</version>
		</dependency>
		<!-- <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> 
			<version>2.5.4</version> </dependency> -->
 
		<!-- <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> 
			<version>3.1.0</version> </dependency> -->
		<!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
		<dependency>
			<groupId>org.freemarker</groupId>
			<artifactId>freemarker</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.1.20</version>
		</dependency>
		<!-- 加上这个才能辨认到log4j2.yml文件 -->
		<dependency>
			<groupId>com.fasterxml.jackson.dataformat</groupId>
			<artifactId>jackson-dataformat-yaml</artifactId>
		</dependency>
		<dependency> <!-- 引入log4j2依赖 -->
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j2</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<exclusions>
				<exclusion>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-starter-logging</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.slf4j</groupId>
					<artifactId>slf4j-log4j12</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> 
			<version>2.11.0</version> </dependency> -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>

​ 2.然后更改test-service的目录结构,删除zk的配置并更改application.yml文件,目录结构与代码:

server:
  port: 38888
spring:
  application: 
      name: test-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
dubbo:
  protocol:
    loadbalance: leastactive
    threadpool: cached
  scan:
    base-packages: org。test.service
  application:
    qos-enable: false
    name: testserver
  registry:
    id: my-registry
    address:  nacos://127.0.0.1:8848
mybatis-plus:
  mapper-locations: classpath:/mapper/*Mapper.xml
  typeAliasesPackage: org.test.entity
  global-config:
    db-config:
      field-strategy: not-empty
      id-type: auto
      db-type: mysql
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true      
    auto-mapping-unknown-column-behavior: none

<img src=“/img/blog/20191202211833.png” alt=“20191202211833” style={{ zoom:‘100%’ }} />

​ 3.再更改registry.conf文件,如果你的nacos是其它服务器,请改成对应都ip跟端口

registry {
  type = "nacos"
  file {
    name = "file.conf"
  }
   zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
    nacos {
    serverAddr = "localhost"
    namespace = ""
    cluster = "default"
  }
}
config {
  type = "nacos"
  file {
    name = "file.conf"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
    nacos {
    serverAddr = "localhost"
    namespace = ""
    cluster = "default"
  }
}

​ 4.接着我们运行provideApplication

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

启动成功啦,我们再去看seata的日志:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

成功了,这下我们一样,去修改test-client的内容,首先一样application.yml,把zk换成nacos,这里就不详细描述了,把test-service内的registry.conf,复制到client项目的resources中覆盖原来的registry.conf.

然后我们可以运行clientApplication:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 5.确认服务已经被发布并测试事务运行是否正常

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

服务成功发布出来,也被成功消费了.这下我们再去swagger中去测试回滚是否一切正常,访问http://127.0.0.1:28888/swagger-ui.html

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

恭喜你,看到这一定跟我一样成功了!

总结

关于nacos的使用跟seata的简单搭建已经完成了,更详细的内容希望希望大家访问以下地址阅读详细文档

nacos官网

dubbo官网

seata官网

<think>我们在使用Spring和MyBatis时,分布式事务管理是一个关键问题。分布式事务涉及多个数据库或服务的操作,需要确保所有操作要么全部成功,要么全部失败。常见的解决方案是使用JTA(JavaTransactionAPI)实现,而Atomikos是一个流行的JTA事务管理器实现。根据引用[1]和引用[2],我们可以通过以下步骤实现Spring+MyBatis+Atomikos的分布式事务管理:###1.配置JTA事务管理器(Atomikos)在Spring配置文件中,需要配置Atomikos的事务管理器。引用[1]中使用了`JtaTransactionManager`,并指定了JNDI名称。但我们通常不通过JNDI查找,而是直接配置Atomikos的`UserTransactionManager`和`UserTransactionImp`。```xml<beanid="atomikosTransactionManager"class="com.atomikos.icatch.jta.UserTransactionManager"init-method="init"destroy-method="close"><propertyname="forceShutdown"value="true"/></bean><beanid="atomikosUserTransaction"class="com.atomikos.icatch.jta.UserTransactionImp"><propertyname="transactionTimeout"value="300"/></bean><beanid="transactionManager"class="org.springframework.transaction.jta.JtaTransactionManager"><propertyname="transactionManager"ref="atomikosTransactionManager"/><propertyname="userTransaction"ref="atomikosUserTransaction"/></bean>```###2.配置多个数据源在分布式事务中,通常有多个数据源。每个数据源需要配置为支持XA协议(即分布式事务)。例如,为两个不同的数据库配置数据源:```xml<beanid="dataSource1"class="com.atomikos.jdbc.AtomikosDataSourceBean"init-method="init"destroy-method="close"><propertyname="uniqueResourceName"value="mysql1"/><propertyname="xaDataSourceClassName"value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/><propertyname="xaProperties"><props><propkey="url">jdbc:mysql://localhost:3306/db1</prop><propkey="user">root</prop><propkey="password">root</prop></props></property><propertyname="poolSize"value="10"/></bean><beanid="dataSource2"class="com.atomikos.jdbc.AtomikosDataSourceBean"init-method="init"destroy-method="close"><propertyname="uniqueResourceName"value="mysql2"/><propertyname="xaDataSourceClassName"value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/><propertyname="xaProperties"><props><propkey="url">jdbc:mysql://localhost:3306/db2</prop><propkey="user">root</prop><propkey="password">root</prop></props></property><propertyname="poolSize"value="10"/></bean>```###3.配置MyBatis的SqlSessionFactory为每个数据源配置SqlSessionFactory:```xml<beanid="sqlSessionFactory1"class="org.mybatis.spring.SqlSessionFactoryBean"><propertyname="dataSource"ref="dataSource1"/><!--其他配置如mapperLocations等--></bean><beanid="sqlSessionFactory2"class="org.mybatis.spring.SqlSessionFactoryBean"><propertyname="dataSource"ref="dataSource2"/><!--其他配置--></bean>```###4.配置事务注解支持启用Spring的注解驱动事务管理:```xml<tx:annotation-driventransaction-manager="transactionManager"/>```然后在服务层方法上使用`@Transactional`注解,该方法中涉及多个数据源的操作将在一个分布式事务中执行。###5.编写业务代码在业务方法中,同时操作多个数据源,并使用`@Transactional`注解标记方法。引用[2]中的例子:```java@ServicepublicclassAccountService{@AutowiredprivateD1Daod1Dao;//使用数据源1的Dao@AutowiredprivateD2Daod2Dao;//使用数据源2的Dao@TransactionalpublicvoidupdateAccount(Integeraccount){intuserAId=1;intuserBId=2;intuserA_Account=d1Dao.getAccount(userAId);intuserB_Account=d2Dao.getAccount(userBId);d1Dao.saveAccount(userAId,userA_Account+account);d2Dao.saveAccount(userBId,userB_Account-account);if(userB_Account-account<0){thrownewAccountNotEnoughException();//抛出异常,触发回滚}}}```当方法中抛出异常时,事务会回滚,所有数据源的操作都会被撤销。###注意事项-使用Atomikos需要添加相关依赖,如`transactions-jta`、`atomikos-util`和`atomikos-transactions-api`等。-确保数据库驱动支持XA,并且数据库服务器本身支持XA事务(如MySQL需要InnoDB引擎)。-分布式事务性能开销较大,应避免在事务中包含耗时操作。通过以上步骤,我们实现了Spring、MyBatis和Atomikos的集成,管理分布式事务。§§相关问题§§1.分布式事务中常见的问题有哪些?2.除了Atomikos,还有哪些分布式事务解决方案?3.Spring事务传播行为在分布式事务中如何应用?</think> ### Spring与MyBatis分布式事务实现及解决方案 在分布式系统中,Spring与MyBatis整合实现分布式事务的核心是**JTA(Java Transaction API)** 和**XA协议**。以下是实现分布式事务的三种主要方案: #### 方案1:Atomikos (JTA事务管理器) ```xml <!-- 配置示例 (简化版) --> <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"> <property name="forceShutdown" value="true"/> </bean> <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"/> <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager" ref="atomikosTransactionManager"/> <property name="userTransaction" ref="atomikosUserTransaction"/> </bean> <!-- XA数据源配置 --> <bean id="dataSource1" class="com.atomikos.jdbc.AtomikosDataSourceBean"> <property name="uniqueResourceName" value="mysql-ds1"/> <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/> </bean> ``` **工作原理**: 1. 使用Atomikos作为JTA事务管理器 2. 配置XA兼容的数据源(需数据库支持XA协议) 3. 在业务方法中使用`@Transactional`注解 4. 事务提交时通过**两阶段提交协议(2PC)** 协调多资源 #### 方案2:Seata (AT模式) ```java // 业务方法示例 @GlobalTransactional // Seata分布式事务注解 public void transferFunds() { accountService.deduct(); // 服务A inventoryService.reduce(); // 服务B } ``` **特点**: - 基于**补偿机制**而非2PC - 支持`AT`、`TCC`、`Saga`等模式 - 无侵入式配置,适用于微服务架构 #### 方案3:Spring Cloud + Alibaba Nacos ```yaml # application.yml配置 spring: cloud: alibaba: seata: tx-service-group: my_tx_group ``` ### 关键问题与解决方案 #### 1. **事务失效场景** - **问题**:MyBatis本地事务与分布式事务冲突 - **解决**: ```xml <!-- 禁用MyBatis自动提交 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configuration"> <bean class="org.apache.ibatis.session.Configuration"> <property name="autoCommit" value="false"/> <!-- 关键配置 --> </bean> </property> </bean> ``` #### 2. **连接池配置** ```xml <bean id="dataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"> <property name="maxPoolSize" value="50"/> <!-- 避免连接泄漏 --> <property name="testQuery" value="SELECT 1"/> <!-- 连接验证 --> </bean> ``` #### 3. **超时处理** ```java @Transactional(timeout = 30) // 单位:秒 public void distributedOperation() {...} ``` ### 最佳实践 1. **数据源选择**:MySQL需启用InnoDB引擎并开启XA支持 2. **性能优化**: - 避免长事务(事务时间<3秒) - 使用Seata的AT模式降低2PC开销 3. **降级方案**: ```java @Transactional(rollbackFor = Exception.class) public void orderProcess() { try { // 分布式事务操作 } catch (Exception e) { // 记录操作日志,启动补偿任务 compensationService.scheduleCompensation(); throw e; } } ``` ### 典型场景示例 **银行转账业务**(参考引用[2]优化版): ```java @GlobalTransactional public void transfer(int fromId, int toId, BigDecimal amount) { // 跨数据库操作 accountDao.deduct(fromId, amount); // 数据库A accountDao.add(toId, amount); // 数据库B if(accountDao.getBalance(toId).compareTo(BigDecimal.ZERO) < 0) { throw new InsufficientBalanceException(); // 触发全局回滚 } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值