使用Spring Boot實現分散式事務

省赚客开发者团队發表於2024-07-13

使用Spring Boot實現分散式事務

大家好,我是微賺淘客系統3.0的小編,是個冬天不穿秋褲,天冷也要風度的程式猿!

在微服務架構中,分散式事務是一個重要的概念,它用於確保在分散式系統中各個服務之間的資料一致性。分散式事務的實現相對複雜,因為它需要跨越多個服務、資料庫或訊息佇列來維護資料的一致性。本文將詳細介紹如何使用Spring Boot實現分散式事務,重點介紹如何使用Spring Cloud和Seata框架來管理分散式事務。

一、分散式事務的基本概念

分散式事務是指在多個不同的資料庫或服務之間執行的事務操作,這些操作需要保證一致性和完整性。分散式事務通常透過兩階段提交(2PC)、三階段提交(3PC)或TCC(Try-Confirm/Cancel)等協議來實現。

二、Seata簡介

Seata(Simple Extensible Autonomous Transaction Architecture)是阿里巴巴開源的一款分散式事務解決方案,提供了高效且易用的分散式事務服務。Seata支援AT、TCC、SAGA和XA等多種事務模式。

三、環境準備

  1. JDK 8或以上版本
  2. Maven 3或以上版本
  3. MySQL資料庫
  4. Seata Server

四、建立Spring Boot專案

首先,我們使用Spring Initializr建立一個新的Spring Boot專案,並新增必要的依賴。

  1. 開啟 Spring Initializr
  2. 輸入專案資訊:
    • Group: cn.juwatech
    • Artifact: spring-boot-seata
  3. 新增依賴:
    • Spring Web
    • Spring Data JPA
    • MySQL Driver
    • Seata Spring Boot Starter

點選“Generate”按鈕生成專案並下載,然後解壓專案檔案。

五、配置Seata

  1. src/main/resources 目錄下建立 application.yml 配置檔案,新增以下內容:
server:
  port: 8080

spring:
  application:
    name: spring-boot-seata
  datasource:
    url: jdbc:mysql://localhost:3306/seata_example
    username: root
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

seata:
  enabled: true
  application-id: spring-boot-seata
  tx-service-group: my_test_tx_group
  1. src/main/resources 目錄下建立 seata.conf 配置檔案,新增以下內容:
transport {
  type = "TCP"
  server = "NIO"
  heartbeat = "true"
  serialization = "seata"
  compressor = "none"
}

service {
  vgroupMapping.my_test_tx_group = "default"
  enableDegrade = false
  disableGlobalTransaction = false
}

client {
  rm {
    asyncCommitBufferLimit = 10000
    lock {
      retryTimes = 30
      retryInterval = 10
    }
    reportRetryCount = 5
    tableMetaCheckEnable = false
  }

  tm {
    commitRetryCount = 5
    rollbackRetryCount = 5
  }

  undo {
    dataValidation = true
    logSerialization = "jackson"
    onlyCareUpdateColumns = true
  }
}

support {
  spring {
    datasource-autoproxy = false
  }
}

六、編寫業務邏輯

  1. 建立實體類 OrderAccount
package cn.juwatech.seata.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String userId;
    private String productId;
    private Integer count;
    private Double money;

    // getters and setters
}
package cn.juwatech.seata.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Account {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String userId;
    private Double balance;

    // getters and setters
}
  1. 建立 OrderRepositoryAccountRepository
package cn.juwatech.seata.repository;

import cn.juwatech.seata.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;

public interface OrderRepository extends JpaRepository<Order, Long> {
}
package cn.juwatech.seata.repository;

import cn.juwatech.seata.entity.Account;
import org.springframework.data.jpa.repository.JpaRepository;

public interface AccountRepository extends JpaRepository<Account, Long> {
    Account findByUserId(String userId);
}
  1. 建立服務類 OrderServiceAccountService
package cn.juwatech.seata.service;

import cn.juwatech.seata.entity.Order;
import cn.juwatech.seata.repository.OrderRepository;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @GlobalTransactional(name = "my_test_tx_group", rollbackFor = Exception.class)
    public void createOrder(Order order) {
        orderRepository.save(order);
    }
}
package cn.juwatech.seata.service;

import cn.juwatech.seata.entity.Account;
import cn.juwatech.seata.repository.AccountRepository;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccountService {

    @Autowired
    private AccountRepository accountRepository;

    @GlobalTransactional(name = "my_test_tx_group", rollbackFor = Exception.class)
    public void decreaseBalance(String userId, double amount) {
        Account account = accountRepository.findByUserId(userId);
        if (account != null && account.getBalance() >= amount) {
            account.setBalance(account.getBalance() - amount);
            accountRepository.save(account);
        } else {
            throw new RuntimeException("Insufficient balance");
        }
    }
}
  1. 建立控制器 OrderController
package cn.juwatech.seata.controller;

import cn.juwatech.seata.entity.Order;
import cn.juwatech.seata.service.AccountService;
import cn.juwatech.seata.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/orders")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @Autowired
    private AccountService accountService;

    @PostMapping("/create")
    public String createOrder(@RequestBody Order order) {
        orderService.createOrder(order);
        accountService.decreaseBalance(order.getUserId(), order.getMoney());
        return "Order created successfully";
    }
}

七、執行並驗證

啟動 Spring Boot 應用程式,並驗證分散式事務功能。

  1. 執行 SpringBootSeataApplication 類,啟動 Spring Boot 應用程式。
  2. 使用 Postman 或其他工具傳送 POST 請求到 http://localhost:8080/orders/create,請求體為:
{
    "userId": "1",
    "productId": "P1001",
    "count": 2,
    "money": 200.00
}
  1. 檢查 MySQL 資料庫,驗證訂單和賬戶資訊是否正確更新。

透過以上步驟,我們成功地將 Spring Boot 應用與 Seata 整合,實現了分散式事務管理。Seata 提供了強大的分散式事務管理功能,使得在微服務架構中實現資料一致性變得更加容易和高效。

著作權歸聚娃科技微賺淘客系統開發者團隊,轉載請註明出處!

相關文章