Seata解决分布式事务

我举的例子是:在网上购物时,我们支付后,订单微服务会更新订单状态,同时会远程调用购物车微服务清空购物车,和调用商品微服务完成商品库存减一。

我们曾经说的事务是只能在本微服务完成回滚,意思就是如果过程中出现问题需要回滚,只有更新订单状态 这一操作会回滚,而清空购物车和商品库存减一 这俩操作是远程调用其他微服务的操作,是不能回滚的,所以我们就需要分布式事务来解决这种问题。

Seata:致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。

Seata事务管理中有3个重要角色:

 TC:事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。

 TM:事务管理器:定义全局事务范围、开始全局事务、提交或回滚全局事务。

 RM:资源管理器:管理分支事务,与TC交谈以注册分支事务和报告分支事务的状态。

 seata有俩种模式:

 XA:

 

在2.3步之前的操作都是对数据库进行预处理,只有2.3确定全部成功后,才会commit提交。

优点:事务强一致性,满足acid原则;常用数据库都支持,实现简单 没有代码介入。

缺点:在若干个RM执行sql时,需要锁定数据库资源,不同的RM用的时间也不同,若有一个还没执行完,其余的执行完的RM就只能干等着,性能很差;依赖关系型数据库实现事务。

 AT模式:

 与XA模式不同的是:AT模式的1.4步执行sql会立刻提交,在1.4执行sql前会基于数据库信息形成一个快照,如果后面有RM失败,就可以把快照信息写回数据库恢复数据。

缺点:在1.4执行提交后,并且在第二阶段事务失败需要回滚时,期间会有短暂(虽然时间很短)的数据不一致,只满足了最终一致性。

下面我来说一说怎么用Java代码去实现它:

 首先我们需要去下载seata,我用的是docker 部署的,用的是1.5.2版本。我们需要把seata注册到nacos中。

我们写一下seata的配置文件: 这里面你只需要改nacos的地址、账号密码;mysql地址、账号密码。

server:
  port: 7099

spring:
  application:
    name: seata-server
        
logging:
  config: classpath:logback-spring.xml
  file:
    path: ${user.home}/logs/seata
  # extend:
  #   logstash-appender:
  #     destination: 127.0.0.1:4560
  #   kafka-appender:
  #     bootstrap-servers: 127.0.0.1:9092
  #     topic: logback_to_logstash

console: #seata控制台的账号密码
  user:
    username: seata
    password: seata

seata:
  config:
    # support: nacos, consul, apollo, zk, etcd3
    type: file
    # type: nacos
    # nacos:
    #   server-addr: 192.168.1.105:8848
    #   group : "DEFAULT_GROUP"
    #   namespace: ""
    #   dataId: "shared-seata.yaml"
    #   username: "nacos"
    #   password: "nacos"
  registry:
    # support: nacos, eureka, redis, zk, consul, etcd3, sofa
    type: nacos

    nacos:
      application: seata-server
      server-addr: host.docker.internal:8848 #nacos 地址
      group: "DEFAULT_GROUP"
      namespace: ""
      username: "nacos"
      password: "nacos"
      
#  server:
#    service-port: 8091 #If not configured, the default is '${server.port} + 1000'
  security:
    secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
    tokenValidityInMilliseconds: 1800000
    ignore:
      urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/api/v1/auth/login
  server:
    # service-port: 8091 #If not configured, the default is '${server.port} + 1000'
    max-commit-retry-timeout: -1
    max-rollback-retry-timeout: -1
    rollback-retry-timeout-unlock-enable: false
    enable-check-auth: true
    enable-parallel-request-handle: true
    retry-dead-threshold: 130000
    xaer-nota-retry-timeout: 60000
    enableParallelRequestHandle: true
    recovery:
      committing-retry-period: 1000
      async-committing-retry-period: 1000
      rollbacking-retry-period: 1000
      timeout-retry-period: 1000
    undo:
      log-save-days: 7
      log-delete-period: 86400000
    session:
      branch-async-queue-size: 5000 #branch async remove queue size
      enable-branch-async-remove: false #enable to asynchronous remove branchSession
  store:
    # support: file 、 db 、 redis
    mode: db
    session:
      mode: db
    lock:
      mode: db
    db:
      datasource: druid
      db-type: mysql
      driver-class-name: com.mysql.cj.jdbc.Driver
      #数据库地址
      url: jdbc:mysql://mysql:3306/seata?rewriteBatchedStatements=true&serverTimezone=UTC
      user: root
      password: 123
      min-conn: 10
      max-conn: 100
      global-table: global_table
      branch-table: branch_table
      lock-table: lock_table
      distributed-lock-table: distributed_lock
      query-limit: 1000
      max-wait: 5000
    # redis:
    #   mode: single
    #   database: 0
    #   min-conn: 10
    #   max-conn: 100
    #   password:
    #   max-total: 100
    #   query-limit: 1000
    #   single:
    #     host: 192.168.150.101
    #     port: 6379
  metrics:
    enabled: false
    registry-type: compact
    exporter-list: prometheus
    exporter-prometheus-port: 9898
    bindIpAddr: ${SERVICE_BIND_IP}
  transport:
    rpc-tc-request-timeout: 15000
    enable-tc-server-batch-send-response: false
    shutdown:
      wait: 3
    thread-factory:
      boss-thread-prefix: NettyBoss
      worker-thread-prefix: NettyServerNIOWorker
      boss-thread-size: 1

然后docker启动: 其中SEATA_IP最好与你的nacos启动地址一致,这里查看nacos地址:

如果你nacos和mysql也是用docker部署的,就一定要让它们三个加入同一network。

docker run --name seata -p 8099:8099 -p 7099:7099 -e SEATA_IP=注册到nacos中的地址 -v 你的配置文件的目录:/seata-server/resources --privileged=true --network 你的network名称 -d seataio/seata-server:1.5.2

启动成功后,我们就可以在nacos中看到我们的seata服务:

 然后我们需要在nacos中写一些配置来让我们的微服务能注册到seata中:

seata:
  registry:  #TC服务中心的配置,微服务根据这些配置去注册中心获取TC服务地址
    type: nacos
    nacos: 
      server-addr: 127.0.0.1:8848
      namespace: ""
      group: DEFAULT_GROUP
      application: seata-server
      username: nacos
      password: nacos
      #   事务组名称
  tx-service-group: hmall    
  service:
    vgroup-mapping:  # 事务组与tc集群的映射关系
      hmall: "default"
    grouplist: 
     default: 127.0.0.1:8099

  #data-source-proxy-mode: XA   #不填,默认就是AT

然后我们就可以去稍微改一改我们的Java代码了:

首先为我们的购物车微服务、订单微服务、商品微服务添加seata依赖:

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        </dependency>

还要在这三个微服务的配置文件中导入nacos中的seata配置: 

spring:
  application:
    name: ***  # 服务名称
  profiles:
    active: dev # 开发环境
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos地址
      config:
        file-extension: yaml
        shared-configs: #seata配置 
          - data-id: shared-seata.yaml
      discovery:
        cluster-name: public

 

在网上购物时,我们支付后,订单微服务会更新订单状态,同时会远程调用购物车微服务清空购物车,和调用商品微服务完成商品库存减一。   这一操作是订单微服务中的方法,找到改service层方法加上@GlobalTransactional注解

    @Override
    @GlobalTransactional
    public Long createOrder(OrderFormDTO orderFormDTO) {
       .......
    }

我们还要找到购物车微服务的清空购物车的service方法和商品微服务的库存减一的service方法,为它们加上@Transactional注解.

然后我们就可以启动我们的微服务了,我们每成功启动并注册到seata中,seata的启动日志就会在最下面打印这几行日志:

 由此可知我们微服务成功启动了,并注册到了seata中。

  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Seata是一个开源的分布式事务解决方案,它提供了一套简单易用的API,可以帮助解决分布式事务的问题。Seata使用了两阶段提交协议(Two-Phase Commit,简称2PC)来确保分布式事务的一致性。它由三个核心组件组成:事务协调器(Transaction Coordinator)、资源管理器(Resource Manager)和事务参与者(Transaction Participant)。 事务协调器负责协调和管理全局事务,它负责事务的发起、提交和回滚操作。资源管理器负责管理分支事务的提交和回滚,它与具体的数据源进行交互。事务参与者是分支事务的参与者,它负责执行具体的业务逻辑操作。 当一个分布式事务开始时,事务协调器会生成一个全局事务ID,并将这个ID传递给各个事务参与者。每个事务参与者都会将这个ID关联到自己的本地事务。在事务执行过程,各个事务参与者会将本地事务的操作记录到日志。当所有的事务参与者都执行完毕后,事务协调器会向各个参与者发送提交或回滚的指令,并根据各个参与者的反馈结果来决定最终的事务提交或回滚。 Seata支持多种应用场景,包括数据库事务、消息事务以及跨多个微服务的分布式事务。它提供了与各种常见的间件和框架的集成,如MySQL、Oracle、Dubbo、Spring Cloud等,使得在这些环境下使用Seata非常方便。 总而言之,Seata是一个强大的分布式事务解决方案,可以帮助开发人员简化分布式事务的管理和处理,并保证数据的一致性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值