日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

seata xid是什么_使用Seata彻底解决Spring Cloud中的分布式事务问题!

發(fā)布時(shí)間:2023/12/4 javascript 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 seata xid是什么_使用Seata彻底解决Spring Cloud中的分布式事务问题! 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Seata是Alibaba開源的一款分布式事務(wù)解決方案,致力于提供高性能和簡單易用的分布式事務(wù)服務(wù),本文將通過一個(gè)簡單的下單業(yè)務(wù)場(chǎng)景來對(duì)其用法進(jìn)行詳細(xì)介紹。

什么是分布式事務(wù)問題?

單體應(yīng)用

單體應(yīng)用中,一個(gè)業(yè)務(wù)操作需要調(diào)用三個(gè)模塊完成,此時(shí)數(shù)據(jù)的一致性由本地事務(wù)來保證。

微服務(wù)應(yīng)用

隨著業(yè)務(wù)需求的變化,單體應(yīng)用被拆分成微服務(wù)應(yīng)用,原來的三個(gè)模塊被拆分成三個(gè)獨(dú)立的應(yīng)用,分別使用獨(dú)立的數(shù)據(jù)源,業(yè)務(wù)操作需要調(diào)用三個(gè)服務(wù)來完成。此時(shí)每個(gè)服務(wù)內(nèi)部的數(shù)據(jù)一致性由本地事務(wù)來保證,但是全局的數(shù)據(jù)一致性問題沒法保證。

小結(jié)

在微服務(wù)架構(gòu)中由于全局?jǐn)?shù)據(jù)一致性沒法保證產(chǎn)生的問題就是分布式事務(wù)問題。簡單來說,一次業(yè)務(wù)操作需要操作多個(gè)數(shù)據(jù)源或需要進(jìn)行遠(yuǎn)程調(diào)用,就會(huì)產(chǎn)生分布式事務(wù)問題。

Seata簡介

Seata 是一款開源的分布式事務(wù)解決方案,致力于提供高性能和簡單易用的分布式事務(wù)服務(wù)。Seata 將為用戶提供了 AT、TCC、SAGA 和 XA 事務(wù)模式,為用戶打造一站式的分布式解決方案。

Seata原理和設(shè)計(jì)

定義一個(gè)分布式事務(wù)

我們可以把一個(gè)分布式事務(wù)理解成一個(gè)包含了若干分支事務(wù)的全局事務(wù),全局事務(wù)的職責(zé)是協(xié)調(diào)其下管轄的分支事務(wù)達(dá)成一致,要么一起成功提交,要么一起失敗回滾。此外,通常分支事務(wù)本身就是一個(gè)滿足ACID的本地事務(wù)。這是我們對(duì)分布式事務(wù)結(jié)構(gòu)的基本認(rèn)識(shí),與 XA 是一致的。

協(xié)議分布式事務(wù)處理過程的三個(gè)組件Transaction Coordinator (TC):事務(wù)協(xié)調(diào)器,維護(hù)全局事務(wù)的運(yùn)行狀態(tài),負(fù)責(zé)協(xié)調(diào)并驅(qū)動(dòng)全局事務(wù)的提交或回滾;

Transaction Manager (TM):控制全局事務(wù)的邊界,負(fù)責(zé)開啟一個(gè)全局事務(wù),并最終發(fā)起全局提交或全局回滾的決議;

Resource Manager (RM):控制分支事務(wù),負(fù)責(zé)分支注冊(cè)、狀態(tài)匯報(bào),并接收事務(wù)協(xié)調(diào)器的指令,驅(qū)動(dòng)分支(本地)事務(wù)的提交和回滾。

一個(gè)典型的分布式事務(wù)過程TM 向 TC 申請(qǐng)開啟一個(gè)全局事務(wù),全局事務(wù)創(chuàng)建成功并生成一個(gè)全局唯一的 XID;

XID 在微服務(wù)調(diào)用鏈路的上下文中傳播;

RM 向 TC 注冊(cè)分支事務(wù),將其納入 XID 對(duì)應(yīng)全局事務(wù)的管轄;

TM 向 TC 發(fā)起針對(duì) XID 的全局提交或回滾決議;

TC 調(diào)度 XID 下管轄的全部分支事務(wù)完成提交或回滾請(qǐng)求。

seata-server的安裝與配置我們先從官網(wǎng)下載seata-server,這里下載的是seata-server-0.9.0.zip,下載地址:https://github.com/seata/seata/releases

解壓seata-server安裝包到指定目錄,修改conf目錄下的file.conf配置文件,主要修改自定義事務(wù)組名稱,事務(wù)日志存儲(chǔ)模式為db及數(shù)據(jù)庫連接信息;service {

#vgroup->rgroup

vgroup_mapping.fsp_tx_group = "default" #修改事務(wù)組名稱為:fsp_tx_group,和客戶端自定義的名稱對(duì)應(yīng)

#only support single node

default.grouplist = "127.0.0.1:8091"

#degrade current not support

enableDegrade = false

#disable

disable = false

#unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent

max.commit.retry.timeout = "-1"

max.rollback.retry.timeout = "-1"

}

## transaction log store

store {

## store mode: file、db

mode = "db" #修改此處將事務(wù)信息存儲(chǔ)到數(shù)據(jù)庫中

## database store

db {

## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.

datasource = "dbcp"

## mysql/oracle/h2/oceanbase etc.

db-type = "mysql"

driver-class-name = "com.mysql.jdbc.Driver"

url = "jdbc:mysql://localhost:3306/seat-server" #修改數(shù)據(jù)庫連接地址

user = "root" #修改數(shù)據(jù)庫用戶名

password = "root" #修改數(shù)據(jù)庫密碼

min-conn = 1

max-conn = 3

global.table = "global_table"

branch.table = "branch_table"

lock-table = "lock_table"

query-limit = 100

}

}由于我們使用了db模式存儲(chǔ)事務(wù)日志,所以我們需要?jiǎng)?chuàng)建一個(gè)seat-server數(shù)據(jù)庫,建表sql在seata-server的/conf/db_store.sql中;

修改conf目錄下的registry.conf配置文件,指明注冊(cè)中心為nacos,及修改nacos連接信息即可;registry {

# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa

type = "nacos" #改為nacos

nacos {

serverAddr = "localhost:8848" #改為nacos的連接地址

namespace = ""

cluster = "default"

}

}先啟動(dòng)Nacos,再使用seata-server中/bin/seata-server.bat文件啟動(dòng)seata-server。

數(shù)據(jù)庫準(zhǔn)備

創(chuàng)建業(yè)務(wù)數(shù)據(jù)庫seat-order:存儲(chǔ)訂單的數(shù)據(jù)庫;

seat-storage:存儲(chǔ)庫存的數(shù)據(jù)庫;

seat-account:存儲(chǔ)賬戶信息的數(shù)據(jù)庫。

初始化業(yè)務(wù)表

order表CREATE TABLE `order` (

`id` bigint(11) NOT NULL AUTO_INCREMENT,

`user_id` bigint(11) DEFAULT NULL COMMENT '用戶id',

`product_id` bigint(11) DEFAULT NULL COMMENT '產(chǎn)品id',

`count` int(11) DEFAULT NULL COMMENT '數(shù)量',

`money` decimal(11,0) DEFAULT NULL COMMENT '金額',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

ALTER TABLE `order` ADD COLUMN `status` int(1) DEFAULT NULL COMMENT '訂單狀態(tài):0:創(chuàng)建中;1:已完結(jié)' AFTER `money` ;

storage表CREATE TABLE `storage` (

`id` bigint(11) NOT NULL AUTO_INCREMENT,

`product_id` bigint(11) DEFAULT NULL COMMENT '產(chǎn)品id',

`total` int(11) DEFAULT NULL COMMENT '總庫存',

`used` int(11) DEFAULT NULL COMMENT '已用庫存',

`residue` int(11) DEFAULT NULL COMMENT '剩余庫存',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

INSERT INTO `seat-storage`.`storage` (`id`, `product_id`, `total`, `used`, `residue`) VALUES ('1', '1', '100', '0', '100');

account表CREATE TABLE `account` (

`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'id',

`user_id` bigint(11) DEFAULT NULL COMMENT '用戶id',

`total` decimal(10,0) DEFAULT NULL COMMENT '總額度',

`used` decimal(10,0) DEFAULT NULL COMMENT '已用余額',

`residue` decimal(10,0) DEFAULT '0' COMMENT '剩余可用額度',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

INSERT INTO `seat-account`.`account` (`id`, `user_id`, `total`, `used`, `residue`) VALUES ('1', '1', '1000', '0', '1000');

創(chuàng)建日志回滾表

使用Seata還需要在每個(gè)數(shù)據(jù)庫中創(chuàng)建日志表,建表sql在seata-server的/conf/db_undo_log.sql中。

完整數(shù)據(jù)庫示意圖

制造一個(gè)分布式事務(wù)問題

這里我們會(huì)創(chuàng)建三個(gè)服務(wù),一個(gè)訂單服務(wù),一個(gè)庫存服務(wù),一個(gè)賬戶服務(wù)。當(dāng)用戶下單時(shí),會(huì)在訂單服務(wù)中創(chuàng)建一個(gè)訂單,然后通過遠(yuǎn)程調(diào)用庫存服務(wù)來扣減下單商品的庫存,再通過遠(yuǎn)程調(diào)用賬戶服務(wù)來扣減用戶賬戶里面的余額,最后在訂單服務(wù)中修改訂單狀態(tài)為已完成。該操作跨越三個(gè)數(shù)據(jù)庫,有兩次遠(yuǎn)程調(diào)用,很明顯會(huì)有分布式事務(wù)問題。

客戶端配置對(duì)seata-order-service、seata-storage-service和seata-account-service三個(gè)seata的客戶端進(jìn)行配置,它們配置大致相同,我們下面以seata-order-service的配置為例;

修改application.yml文件,自定義事務(wù)組的名稱;spring:

cloud:

alibaba:

seata:

tx-service-group: fsp_tx_group #自定義事務(wù)組名稱需要與seata-server中的對(duì)應(yīng)添加并修改file.conf配置文件,主要是修改自定義事務(wù)組名稱;service {

#vgroup->rgroup

vgroup_mapping.fsp_tx_group = "default" #修改自定義事務(wù)組名稱

#only support single node

default.grouplist = "127.0.0.1:8091"

#degrade current not support

enableDegrade = false

#disable

disable = false

#unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent

max.commit.retry.timeout = "-1"

max.rollback.retry.timeout = "-1"

disableGlobalTransaction = false

}添加并修改registry.conf配置文件,主要是將注冊(cè)中心改為nacos;registry {

# file 、nacos 、eureka、redis、zk

type = "nacos" #修改為nacos

nacos {

serverAddr = "localhost:8848" #修改為nacos的連接地址

namespace = ""

cluster = "default"

}

}在啟動(dòng)類中取消數(shù)據(jù)源的自動(dòng)創(chuàng)建:@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

@EnableDiscoveryClient

@EnableFeignClients

public class SeataOrderServiceApplication{

public static void main(String[] args){

SpringApplication.run(SeataOrderServiceApplication.class, args);

}

}創(chuàng)建配置使用Seata對(duì)數(shù)據(jù)源進(jìn)行代理:/**

* 使用Seata對(duì)數(shù)據(jù)源進(jìn)行代理

* Created by macro on 2019/11/11.

*/

@Configuration

public class DataSourceProxyConfig{

@Value("${mybatis.mapperLocations}")

private String mapperLocations;

@Bean

@ConfigurationProperties(prefix = "spring.datasource")

public DataSource druidDataSource(){

return new DruidDataSource();

}

@Bean

public DataSourceProxy dataSourceProxy(DataSource dataSource){

return new DataSourceProxy(dataSource);

}

@Bean

public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception{

SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();

sqlSessionFactoryBean.setDataSource(dataSourceProxy);

sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()

.getResources(mapperLocations));

sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());

return sqlSessionFactoryBean.getObject();

}

}使用@GlobalTransactional注解開啟分布式事務(wù):package com.macro.cloud.service.impl;

import com.macro.cloud.dao.OrderDao;

import com.macro.cloud.domain.Order;

import com.macro.cloud.service.AccountService;

import com.macro.cloud.service.OrderService;

import com.macro.cloud.service.StorageService;

import io.seata.spring.annotation.GlobalTransactional;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

/**

* 訂單業(yè)務(wù)實(shí)現(xiàn)類

* Created by macro on 2019/11/11.

*/

@Service

public class OrderServiceImpl implements OrderService{

private static final Logger LOGGER = LoggerFactory.getLogger(OrderServiceImpl.class);

@Autowired

private OrderDao orderDao;

@Autowired

private StorageService storageService;

@Autowired

private AccountService accountService;

/**

* 創(chuàng)建訂單->調(diào)用庫存服務(wù)扣減庫存->調(diào)用賬戶服務(wù)扣減賬戶余額->修改訂單狀態(tài)

*/

@Override

@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)

public void create(Order order){

LOGGER.info("------->下單開始");

//本應(yīng)用創(chuàng)建訂單

orderDao.create(order);

//遠(yuǎn)程調(diào)用庫存服務(wù)扣減庫存

LOGGER.info("------->order-service中扣減庫存開始");

storageService.decrease(order.getProductId(),order.getCount());

LOGGER.info("------->order-service中扣減庫存結(jié)束:{}",order.getId());

//遠(yuǎn)程調(diào)用賬戶服務(wù)扣減余額

LOGGER.info("------->order-service中扣減余額開始");

accountService.decrease(order.getUserId(),order.getMoney());

LOGGER.info("------->order-service中扣減余額結(jié)束");

//修改訂單狀態(tài)為已完成

LOGGER.info("------->order-service中修改訂單狀態(tài)開始");

orderDao.update(order.getUserId(),0);

LOGGER.info("------->order-service中修改訂單狀態(tài)結(jié)束");

LOGGER.info("------->下單結(jié)束");

}

}

分布式事務(wù)功能演示運(yùn)行seata-order-service、seata-storage-service和seata-account-service三個(gè)服務(wù);

數(shù)據(jù)庫初始信息狀態(tài):

調(diào)用接口進(jìn)行下單操作后查看數(shù)據(jù)庫:http://localhost:8180/order/create?userId=1&productId=1&count=10&money=100

我們?cè)趕eata-account-service中制造一個(gè)超時(shí)異常后,調(diào)用下單接口:/**

* 賬戶業(yè)務(wù)實(shí)現(xiàn)類

* Created by macro on 2019/11/11.

*/

@Service

public class AccountServiceImpl implements AccountService{

private static final Logger LOGGER = LoggerFactory.getLogger(AccountServiceImpl.class);

@Autowired

private AccountDao accountDao;

/**

* 扣減賬戶余額

*/

@Override

public void decrease(Long userId, BigDecimal money){

LOGGER.info("------->account-service中扣減賬戶余額開始");

//模擬超時(shí)異常,全局事務(wù)回滾

try {

Thread.sleep(30*1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

accountDao.decrease(userId,money);

LOGGER.info("------->account-service中扣減賬戶余額結(jié)束");

}

}此時(shí)我們可以發(fā)現(xiàn)下單后數(shù)據(jù)庫數(shù)據(jù)并沒有任何改變;

我們可以在seata-order-service中注釋掉@GlobalTransactional來看看沒有Seata的分布式事務(wù)管理會(huì)發(fā)生什么情況:/**

* 訂單業(yè)務(wù)實(shí)現(xiàn)類

* Created by macro on 2019/11/11.

*/

@Service

public class OrderServiceImpl implements OrderService{

/**

* 創(chuàng)建訂單->調(diào)用庫存服務(wù)扣減庫存->調(diào)用賬戶服務(wù)扣減賬戶余額->修改訂單狀態(tài)

*/

@Override

// ? ?@GlobalTransactional(name = "fsp-create-order",rollbackFor = Exception.class)

public void create(Order order){

LOGGER.info("------->下單開始");

//省略代碼...

LOGGER.info("------->下單結(jié)束");

}

}由于seata-account-service的超時(shí)會(huì)導(dǎo)致當(dāng)庫存和賬戶金額扣減后訂單狀態(tài)并沒有設(shè)置為已經(jīng)完成,而且由于遠(yuǎn)程調(diào)用的重試機(jī)制,賬戶余額還會(huì)被多次扣減。

參考資料

Seata官方文檔:https://github.com/seata/seata/wiki

使用到的模塊springcloud-learning

├── seata-order-service -- 整合了seata的訂單服務(wù)

├── seata-storage-service -- 整合了seata的庫存服務(wù)

└── seata-account-service -- 整合了seata的賬戶服務(wù)

項(xiàng)目源碼地址

https://github.com/macrozheng/springcloud-learning

總結(jié)

以上是生活随笔為你收集整理的seata xid是什么_使用Seata彻底解决Spring Cloud中的分布式事务问题!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。