• Springcloud----Seata分布式事务


    分布式事务的解决办法:

    • 使用消息中间件
    • 手写代码解决分布式事务
    • 使用第三方组件--->Seata阿里巴巴的产品

    这里只介绍通过第三方插件----Seata解决分布式事务的问题.

    一.什么是Seata?

    Seata 是什么Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。https://seata.io/zh-cn/docs/overview/what-is-seata.html

     二.Seata的执行流程

    Seata 的执行流程如下 :
    1. A服务【订单微服务】的TM[事务发起者]TC[seata服务端]申请开启一个全局事务,TC就会创建一个全局事务并返回一个唯一的XID
    2. A服务开始远程调用B服务【账户微服务】,此时XID会在微服务的调用链上传播
    3. B服务的RMTC注册分支事务,并将其纳入XID对应的全局事务的管辖
    4. B服务执行分支事务,向数据库做操作
    5. 全局事务调用链处理完毕,TM根据有无异常向TC发起全局事务的提交或者回滚
    6. TC协调其管辖之下的所有分支事务, 决定是否回滚
    TM :事务发起者【在哪个方法上添加了全局事务注解的】
    TC : 事务管理器【 seata 的服务端】
    RM: 每个操作数据库的微服务
    XID: 全局事务 id
    TM RM 都属于微服务代码
    TC: seata 服务器。

    三.搭建Seata服务器

    特别注意, SpringCloudAlibaba的版本和Seatad以及其他各版本之间是否是兼容的,不代表最新的版本能在你的项目中.我这里SpringCloud的版本是2.2.3,所以我这里使用的Seata的版本是1.3.0

    (1)下载地址:

    Tags · seata/seata · GitHu

     记得选择你能用的版本号下载.参照上表.

    (2) 下载之后将压缩包解压到本地磁盘中.路径最好不要带中文

     这里只有三个目录.我们可以从官网提供的源码中把源码中的script中的目录也粘贴到我们的seata中.最后目录结构会变成这样:

    官网提供的源码地址:

    GitHub - seata/seata at v1.3.0

    记得下载对应得版本,我这里用的是1.3.0,改成你能用得版本.

     这个script文件我们后面会有很大得用处.

     (3) 修改conf/file.conf文件的内容

    未来我们肯定Seata不会只开单机模式.既然要搭建集群的话,我们就需要给数据持久化到数据库中或者缓存中了.Seata提供了三种持久化方式:file,db,redis.这里我们选择持久化到db中,db选择为mysql.所以我们需要修改这个文件中的mode为db.并且修改后面的db中的信息为你自己的数据库信息.

    完整的file.conf文件:

    1. ## transaction log store, only used in seata-server
    2. store {
    3. ## store mode: file、db、redis
    4. mode = "db"
    5. ## file store property
    6. file {
    7. ## store location dir
    8. dir = "sessionStore"
    9. # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    10. maxBranchSessionSize = 16384
    11. # globe session size , if exceeded throws exceptions
    12. maxGlobalSessionSize = 512
    13. # file buffer size , if exceeded allocate new buffer
    14. fileWriteBufferCacheSize = 16384
    15. # when recover batch read size
    16. sessionReloadReadSize = 100
    17. # async, sync
    18. flushDiskMode = async
    19. }
    20. ## database store property
    21. db {
    22. ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
    23. datasource = "druid"
    24. ## mysql/oracle/postgresql/h2/oceanbase etc.
    25. dbType = "mysql"
    26. driverClassName = "com.mysql.cj.jdbc.Driver"
    27. url = "jdbc:mysql://127.0.0.1:3306/seata?serverTimezone=Asia/Shanghai"
    28. user = "root"
    29. password = "xxxxxxxx"
    30. minConn = 5
    31. maxConn = 30
    32. globalTable = "global_table"
    33. branchTable = "branch_table"
    34. lockTable = "lock_table"
    35. queryLimit = 100
    36. maxWait = 5000
    37. }
    38. ## redis store property
    39. redis {
    40. host = "127.0.0.1"
    41. port = "6379"
    42. password = ""
    43. database = "0"
    44. minConn = 1
    45. maxConn = 10
    46. queryLimit = 100
    47. }
    48. }

    注意:mysql5.7以后的版本,driverClassName为"com.mysql.cj.jdbc.Driver",如果是这之前的版本的数据库,不需要修改,默认就为"com.mysql.jdbc.Driver".url这里,我的是mysql8版本以后,url需要加上时区.改完之后保存并退出.

    然后将mysql的jar包放入lib目录下.

    Seata的lib目录默认是没有mysql的jar包的,但是这个jar包是在lib/jdbc目录下的.因为mysql5.1和mysql8为两个版本,这里Seata两个版本都提供了jar包,按需引入,把需要的jar包复制到lib目录下即可.

    (4) 创建数据库并导入表结构

    我们file.conf文件中已经在其中的db中写了我们使用的数据库名为seata,如果你改成了别的,对应的数据库名也要改.数据库建好以后,从我们导入的script\server\db目录下,找到我们的sql文件.

     把表结构导入到我们刚才创建的数据库中.

    最后在数据库中就有了这三张表.

    再说一遍,源码一定要用对应的版本.在1.5版本,数据库中的表就变成了四张,而我们1.3.0版本只有这三张表.一定要注意!

    (5) 指定seata的注册中心地址和配置中心的内容

    ①修改conf目录下的registry.conf文件.将registry和config都改为nacos并且配置nacos的信息

    改成你的nacos的配置信息.如果你想将配置文件不放在默认的public环境中,把你想放入的环境的id写到namespace中,如果就放在public中就不用管他. 分组不改默认就是SEATA_GROUP        

    config:

     完整的registry.conf文件:

    1. registry {
    2. # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
    3. type = "nacos"
    4. nacos {
    5. application = "seata-server"
    6. serverAddr = "127.0.0.1:8888"
    7. group = "SEATA_GROUP"
    8. namespace = ""
    9. cluster = "default"
    10. username = "nacos"
    11. password = "nacos"
    12. }
    13. eureka {
    14. serviceUrl = "http://localhost:8761/eureka"
    15. application = "default"
    16. weight = "1"
    17. }
    18. redis {
    19. serverAddr = "localhost:6379"
    20. db = 0
    21. password = ""
    22. cluster = "default"
    23. timeout = 0
    24. }
    25. zk {
    26. cluster = "default"
    27. serverAddr = "127.0.0.1:2181"
    28. sessionTimeout = 6000
    29. connectTimeout = 2000
    30. username = ""
    31. password = ""
    32. }
    33. consul {
    34. cluster = "default"
    35. serverAddr = "127.0.0.1:8500"
    36. }
    37. etcd3 {
    38. cluster = "default"
    39. serverAddr = "http://localhost:2379"
    40. }
    41. sofa {
    42. serverAddr = "127.0.0.1:9603"
    43. application = "default"
    44. region = "DEFAULT_ZONE"
    45. datacenter = "DefaultDataCenter"
    46. cluster = "default"
    47. group = "SEATA_GROUP"
    48. addressWaitTime = "3000"
    49. }
    50. file {
    51. name = "file.conf"
    52. }
    53. }
    54. config {
    55. # file、nacos 、apollo、zk、consul、etcd3
    56. type = "nacos"
    57. nacos {
    58. serverAddr = "127.0.0.1:8888"
    59. namespace = ""
    60. group = "SEATA_GROUP"
    61. username = "nacos"
    62. password = "nacos"
    63. }
    64. consul {
    65. serverAddr = "127.0.0.1:8500"
    66. }
    67. apollo {
    68. appId = "seata-server"
    69. apolloMeta = "http://192.168.1.204:8801"
    70. namespace = "application"
    71. }
    72. zk {
    73. serverAddr = "127.0.0.1:2181"
    74. sessionTimeout = 6000
    75. connectTimeout = 2000
    76. username = ""
    77. password = ""
    78. }
    79. etcd3 {
    80. serverAddr = "http://localhost:2379"
    81. }
    82. file {
    83. name = "file.conf"
    84. }
    85. }

     ②修改需要放入配置中心的配置内容

    • 修改script\config-center目录下的config.txt文件.

    mode改为db,store.file删不删除都可以.留着也没事.虽然我这里给删了.包括下面的store.redis都可以删的,但是这个我没有删

    •  特别注意这个设置:

     

    其中这个qy151就是分组名.它是seata的分组不是在nacos中的分组,设置分组的作用是可以选择用哪一组的seata服务,以免你这一组的seata宕机了我还可以用别的分组. 记住这里改的分组名,我们后面配置的时候还会用到它.

    完整的config.txt文件:

    1. transport.type=TCP
    2. transport.server=NIO
    3. transport.heartbeat=true
    4. transport.enableClientBatchSendRequest=false
    5. transport.threadFactory.bossThreadPrefix=NettyBoss
    6. transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
    7. transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
    8. transport.threadFactory.shareBossWorker=false
    9. transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
    10. transport.threadFactory.clientSelectorThreadSize=1
    11. transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
    12. transport.threadFactory.bossThreadSize=1
    13. transport.threadFactory.workerThreadSize=default
    14. transport.shutdown.wait=3
    15. service.vgroupMapping.qy151=default
    16. service.default.grouplist=127.0.0.1:8091
    17. service.enableDegrade=false
    18. service.disableGlobalTransaction=false
    19. client.rm.asyncCommitBufferLimit=10000
    20. client.rm.lock.retryInterval=10
    21. client.rm.lock.retryTimes=30
    22. client.rm.lock.retryPolicyBranchRollbackOnConflict=true
    23. client.rm.reportRetryCount=5
    24. client.rm.tableMetaCheckEnable=false
    25. client.rm.sqlParserType=druid
    26. client.rm.reportSuccessEnable=false
    27. client.rm.sagaBranchRegisterEnable=false
    28. client.tm.commitRetryCount=5
    29. client.tm.rollbackRetryCount=5
    30. client.tm.degradeCheck=false
    31. client.tm.degradeCheckAllowTimes=10
    32. client.tm.degradeCheckPeriod=2000
    33. store.mode=db
    34. store.db.datasource=druid
    35. store.db.dbType=mysql
    36. store.db.driverClassName=com.mysql.cj.jdbc.Driver
    37. store.db.url=jdbc:mysql://127.0.0.1:3306/seata?serverTimezone=Asia/Shanghai
    38. store.db.user=root
    39. store.db.password=xxxxxx
    40. store.db.minConn=5
    41. store.db.maxConn=30
    42. store.db.globalTable=global_table
    43. store.db.branchTable=branch_table
    44. store.db.queryLimit=100
    45. store.db.lockTable=lock_table
    46. store.db.maxWait=5000
    47. store.redis.host=127.0.0.1
    48. store.redis.port=6379
    49. store.redis.maxConn=10
    50. store.redis.minConn=1
    51. store.redis.database=0
    52. store.redis.password=null
    53. store.redis.queryLimit=100
    54. server.recovery.committingRetryPeriod=1000
    55. server.recovery.asynCommittingRetryPeriod=1000
    56. server.recovery.rollbackingRetryPeriod=1000
    57. server.recovery.timeoutRetryPeriod=1000
    58. server.maxCommitRetryTimeout=-1
    59. server.maxRollbackRetryTimeout=-1
    60. server.rollbackRetryTimeoutUnlockEnable=false
    61. client.undo.dataValidation=true
    62. client.undo.logSerialization=jackson
    63. client.undo.onlyCareUpdateColumns=true
    64. server.undo.logSaveDays=7
    65. server.undo.logDeletePeriod=86400000
    66. client.undo.logTable=undo_log
    67. client.log.exceptionRate=100
    68. transport.serialization=seata
    69. transport.compressor=none
    70. metrics.enabled=false
    71. metrics.registryType=compact
    72. metrics.exporterList=prometheus
    73. metrics.exporterPrometheusPort=9898

    (6)使用脚本自动导入配置文件到nacos配置中心

    在script\config-center\nacos目录下有一个nacos-config.sh的脚本文件.由于我们目前处于Windows的环境,而.sh文件适合在linux中启动.我们可以用git服务来代替linux.

    在nacos-config.sh文件所在目录,点击右键打开Git Bash Here.

    如果你的nacos的配置都是默认的配置(localhost:8848),直接用git方式打开文件直接运行也可以.如果你像我一样修改了nacos的端口号,那么就需要打开git bash 输入以下命令指定nacos的地址和端口号以及namespace(默认用的public如果没用自定的环境就不用配)

    sh nacos-config.sh -h localhost -p 8888

     参数说明:

    -h:host 默认值为localhost

    -p:port 默认值为8848

    -g:配置分组,默认为'SEATA_GROUP'

    -t:对应nacos的命名空间ID字段,默认值为空

    可以看到我们的配置文件都已经导入了.

    最后启动seata服务器.

    四.配置微服务客户端

    (1) 在每个微服务对应的数据库中创建unlog表.如果你就一个数据库,那就引入一个表就行.

    这个表不需要手动写,script\client\at\db目录下的mysql.sql文件里面有.

    (2) 给需要的微服务(即整个链路上的微服务)都引入seata依赖.这里要注意,你的seata的依赖版本一定要保证是和你用的版本是一致的.就像我用的是1.3.0,项目中引入的依赖也一定要是1.3.0

    1. <dependency>
    2. <groupId>com.alibaba.cloudgroupId>
    3. <artifactId>spring-cloud-starter-alibaba-seataartifactId>
    4. dependency>

     (3) 修改每个微服务的配置文件

     单个微服务的完整配置:

    1. server:
    2. port: 8003
    3. spring:
    4. # 数据源配置
    5. datasource:
    6. url: jdbc:mysql://localhost:3306/seata_storage?serverTimezone=Asia/Shanghai
    7. driver-class-name: com.mysql.cj.jdbc.Driver
    8. username: root
    9. password: password
    10. application:
    11. name: seata-storage
    12. cloud:
    13. nacos:
    14. discovery:
    15. server-addr: localhost:8888
    16. alibaba:
    17. seata:
    18. #该值必须和config.txt文件中的分组名称一致
    19. tx-service-group: qy151
    20. mybatis:
    21. mapperLocations: classpath:mapper/*.xml
    22. logging:
    23. level:
    24. com.ykq.dao: debug
    25. seata:
    26. registry:
    27. nacos:
    28. #指定seata服务器在注册中心的名称 默认seata-server
    29. application: seata-server
    30. username: nacos
    31. password: nacos
    32. #指定seata服务器在注册中心的组名 默认SEATA_GROUP
    33. group: SEATA-GROUP
    34. #指定seata服务器所在的注册中心的地址
    35. server-addr: localhost:8888
    36. config:
    37. nacos:
    38. server-addr: localhost:8888
    39. username: nacos
    40. password: nacos
    41. group: SEATA_GROUP
    42. type: nacos

    在事务的发起者所在微服务上添加分布式事务的注解

    @GlobalTransactional

     重启相关的微服务.测试事务是否成功.

    正常下单:

    数据库中的三张表的数据都进行了修改:

    现在我们个表恢复成一开始的样子.

    然后在下单的业务逻辑中故意加一个异常.如果分布式事务引入成功,那么会显示下单失败,并且三张表的内容都不会修改.

     如果没有加分布式事务,我们的数据库中seata-order的表会修改,其他两个表没有变化.如果加了分布式事务,遇到异常会进行事务回滚,我们三张表都不会有改变.

    下单提示下单失败:

     数据库中:

     证明我们的分布式事务已经配置并使用成功.

  • 相关阅读:
    JS逆向实战23——某市wss URL加密+请求头+ws收发
    修·蓝博士:吃东西前一定要这样清理
    高效案例检索工具,Alpha案例库智慧检索成为律师检索工具首选
    Linux 命令 —— tree
    Git详解及 github与gitlab使用
    学习windows系统让python脚本在后台运行的方法
    人工智能方向专利和论文相关
    Linux下gcc编译器和gdb调试
    如何查看当前开发的Android版本
    [NOIP2009 普及组] 分数线划定
  • 原文地址:https://blog.csdn.net/lrs998563/article/details/126541845