# seata-sample **Repository Path**: zzw6776/seata-sample ## Basic Information - **Project Name**: seata-sample - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-11-29 - **Last Updated**: 2021-11-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 参考文章 项目基于dynamic-datasource示例修改而来,主要修改为增加nacos,feign ## 友情提示 1:项目的各中间件版本号都是于固定匹配的,在项目能跑起来并且没有bug的情况下千万不要私自去升级(尤其是spring-cloud-starter-alibaba-seata不要升级为最新版) 由于目前公司环境是spring-cloud-Greenwich.RELEASE的版本,所以实例以该版本为主 目前版本号对应如下 ```XML org.springframework.cloud spring-cloud-starter-parent Greenwich.RELEASE com.alibaba.cloud spring-cloud-alibaba-dependencies 2.1.4.RELEASE pom import com.baomidou dynamic-datasource-spring-boot-starter 3.4.1 org.mybatis.spring.boot mybatis-spring-boot-starter 2.2.0 com.alibaba druid 1.2.6 com.alibaba druid-spring-boot-starter 1.2.6 io.seata seata-spring-boot-starter 1.4.2 com.alibaba.cloud spring-cloud-starter-alibaba-seata 2.1.4.RELEASE io.seata seata-all io.seata seata-spring-boot-starter ``` # 环境搭建 友情提示:先严格按我的文档来搭建成功,部分解释我会在最后补上,网上其他的教程/官网的教程由于版本的原因有些地方是错误的 ## 1.创建数据库 创建数据库名为`seata`,执行Seata的Github官方源码中提供的的MySQL数据库脚本 MySQL脚本地址:[https://github.com/seata/seata/blob/1.4.2/script/server/db/mysql.sql](https://github.com/seata/seata/blob/1.4.2/script/server/db/mysql.sql) ## 2.nacos搭建,略过 ### 2.1创建nacos命名空间 ### 2.2添加Server配置项: Data Id:seataServer.properties Group:SEATA_GROUP 内容: > 以下参数需修改对应值 store.db.url=jdbc:mysql://xxxx:3306/seata?useUnicode=true&rewriteBatchedStatements=true store.db.user= store.db.password= ```YAML ransport.type=TCP transport.server=NIO transport.heartbeat=true transport.enableClientBatchSendRequest=true transport.threadFactory.bossThreadPrefix=NettyBoss transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler transport.threadFactory.shareBossWorker=false transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector transport.threadFactory.clientSelectorThreadSize=1 transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread transport.threadFactory.bossThreadSize=1 transport.threadFactory.workerThreadSize=default transport.shutdown.wait=3 #service.vgroupMapping.my_test_tx_group=default service.default.grouplist=127.0.0.1:8091 service.enableDegrade=false service.disableGlobalTransaction=false client.rm.asyncCommitBufferLimit=10000 client.rm.lock.retryInterval=10 client.rm.lock.retryTimes=30 client.rm.lock.retryPolicyBranchRollbackOnConflict=true client.rm.reportRetryCount=5 client.rm.tableMetaCheckEnable=false client.rm.tableMetaCheckerInterval=60000 client.rm.sqlParserType=druid client.rm.reportSuccessEnable=false client.rm.sagaBranchRegisterEnable=false client.rm.sagaJsonParser=fastjson client.rm.tccActionInterceptorOrder=-2147482648 client.tm.commitRetryCount=5 client.tm.rollbackRetryCount=5 client.tm.defaultGlobalTransactionTimeout=60000 client.tm.degradeCheck=false client.tm.degradeCheckAllowTimes=10 client.tm.degradeCheckPeriod=2000 client.tm.interceptorOrder=-2147482648 store.mode=db store.lock.mode=file store.session.mode=file #store.publicKey='' store.file.dir=file_store/data store.file.maxBranchSessionSize=16384 store.file.maxGlobalSessionSize=512 store.file.fileWriteBufferCacheSize=16384 store.file.flushDiskMode=async store.file.sessionReloadReadSize=100 store.db.datasource=druid store.db.dbType=mysql store.db.driverClassName=com.mysql.cj.jdbc.Driver store.db.url=jdbc:mysql://xxxx:3306/seata?useUnicode=true&rewriteBatchedStatements=true store.db.user= store.db.password= store.db.minConn=5 store.db.maxConn=30 store.db.globalTable=global_table store.db.branchTable=branch_table store.db.distributedLockTable=distributed_lock store.db.queryLimit=100 store.db.lockTable=lock_table store.db.maxWait=5000 store.redis.mode=single store.redis.single.host=127.0.0.1 store.redis.single.port=6379 store.redis.sentinel.masterName='' store.redis.sentinel.sentinelHosts='' store.redis.maxConn=10 store.redis.minConn=1 store.redis.maxTotal=100 store.redis.database=0 store.redis.password='' store.redis.queryLimit=100 server.recovery.committingRetryPeriod=1000 server.recovery.asynCommittingRetryPeriod=1000 server.recovery.rollbackingRetryPeriod=1000 server.recovery.timeoutRetryPeriod=1000 server.maxCommitRetryTimeout=-1 server.maxRollbackRetryTimeout=-1 server.rollbackRetryTimeoutUnlockEnable=false server.distributedLockExpireTime=10000 client.undo.dataValidation=true client.undo.logSerialization=jackson client.undo.onlyCareUpdateColumns=true server.undo.logSaveDays=7 server.undo.logDeletePeriod=86400000 client.undo.logTable=undo_log client.undo.compress.enable=true client.undo.compress.type=zip client.undo.compress.threshold=64k log.exceptionRate=100 transport.serialization=seata transport.compressor=none metrics.enabled=false metrics.registryType=compact metrics.exporterList=prometheus metrics.exporterPrometheusPort=9898 tcc.fence.logTableName=tcc_fence_log tcc.fence.cleanPeriod=1h ``` ### 2.3添加client配置项 Data Id:seata.properties Group:SEATA_GROUP 内容: ```YAML service.vgroupMapping.my_test_tx_group=default ``` ## 3.seata搭建 #### 3.1创建registry.conf文件 其中namespace 需替换为第一步创建出来的命名空间 > 以下值需修改为nacos对应值 serverAddr = "xxxx:8848" namespace = "b3d34adf-4018-4e31-9cf3-627d806657c5" ```YAML registry { type = "nacos" nacos { application = "seata-server" serverAddr = "xxxx:8848" group = "SEATA_GROUP" namespace = "b3d34adf-4018-4e31-9cf3-627d806657c5" cluster = "default" username = "" password = "" } } config { type = "nacos" nacos { serverAddr = "xxxx:8848" namespace = "b3d34adf-4018-4e31-9cf3-627d806657c5" group = "SEATA_GROUP" username = "" password = "" dataId = "seataServer.properties" } } ``` ### 3.2docker run ```YAML docker run -d --name seata --restart=always -p 8091:8091 \ -e SEATA_IP=部署机器ip \ -e SEATA_CONFIG_NAME=file:/seata-server/resources/registry.conf \ -v 上一步创建的registry.conf位置:/seata-server/resources/registry.conf \ -v 你期望的log文件位置:/root/logs \ seataio/seata-server:1.4.2 ``` 启动成功后,若一切顺利可在nacos的服务列表看到seata-server # java端接入 ## 数据库准备 ### 创建相关数据库 ```YAML CREATE DATABASE IF NOT EXIST seata-order; CREATE DATABASE IF NOT EXIST seata-product; CREATE DATABASE IF NOT EXIST seata-account; ``` > AT模式下每个数据库下脚本相关的表,seata需要undo_log来监测和回滚。 相关的脚本不用自行准备,本工程已在resources/db下面准备好,另外配合多数据源的自动执行脚本功能,应用启动后会自动执行。 ### 修改yml文件 ```YAML server: #项目端口号 port: 8080 spring: cloud: nacos: config: server-addr: xxxx:8848 namespace: b3d34adf-4018-4e31-9cf3-627d806657c5 file-extension: yaml discovery: server-addr: xxxx:8848 namespace: b3d34adf-4018-4e31-9cf3-627d806657c5 application: name: tx-seata-sample-8080 main: allow-bean-definition-overriding: true datasource: dynamic: primary: order strict: true seata: true #开启seata代理,开启后默认每个数据源都代理,如果某个不需要代理可单独关闭 seata-mode: AT #支持XA及AT模式,默认AT datasource: order: username: password: url: jdbc:mysql://xxxx:3306/seata_order?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false driver-class-name: com.mysql.jdbc.Driver schema: classpath:db/schema-order.sql account: username: password: url: jdbc:mysql://xxxx:3306/seata_account?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false driver-class-name: com.mysql.jdbc.Driver schema: classpath:db/schema-account.sql product: username: password: url: jdbc:mysql://xxxx:3306/seata_product?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false driver-class-name: com.mysql.jdbc.Driver schema: classpath:db/schema-product.sql test: username: sa password: "" url: jdbc:h2:mem:test driver-class-name: org.h2.Driver seata: false #这个数据源不需要seata seata: enabled: true tx-service-group: my_test_tx_group #一定要是false enable-auto-data-source-proxy: false registry: type: nacos nacos: server-addr: 192.168.10.48:8848 namespace: b3d34adf-4018-4e31-9cf3-627d806657c5 group: SEATA_GROUP config: type: nacos nacos: server-addr: 192.168.10.48:8848 namespace: b3d34adf-4018-4e31-9cf3-627d806657c5 group: SEATA_GROUP #dataId: seataServer.properties ``` ### 启动项目 # 部分结论 1. AT模式可以支持tidb,但是会造成脏读问题,并且脏读会导致更严重的脏写问题,如 ||| |-|-| |线程1|线程2| |select value where id =1
value=1
value=value+1
set value=2 where id =1|| |本地事务提交,全局事务未提交|select value where id =1
value=2
value=value+1
set value=3 where id =1| |全局事务回滚,此时将会报错无法回滚,因为线程2已经将值改为了3,和最初版本的1无法对应,seata不知道该回滚为什么值|| 具体的代码逻辑在以下位置 io.seata.rm.datasource.undo.AbstractUndoLogManager#undo 在确定AT默认会对你的业务造成什么不可挽回的损失之前不建议使用,还是建议使用XA模式,由于官方并没有给出AT模式和XA模式的性能测试差距,我也懒得测,反正满足我的线上业务场景是够了 1. seata支持高可用,只需要保证nacos以及mysql使用同一套,在多部署的情况下会同时注册到nacos上,客户端会根据映射关系去进行集群的选择,具体映射关系为: 事务分组(seata.tx-service-group的值)→nacos里的seata.properties(seata.config.nacos.dataId的值)里service.vgroupMapping.my_test_tx_group值→seata集群(registry.conf里的cluster值) 结合官方说明可能会更清楚