成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

SpringBoot+Dubbo+Seata 輕松搞定分布式事務(wù)提交數(shù)據(jù)不一致難題!

云計(jì)算 分布式
如果當(dāng)前的服務(wù)工程采用的是 openFeign 來實(shí)現(xiàn)服務(wù)遠(yuǎn)程調(diào)用,也可以通過集成spring-cloud-starter-alibaba-seata依賴包實(shí)現(xiàn)分布式事務(wù)操作,其實(shí)現(xiàn)原理也是在遠(yuǎn)程調(diào)用的請求頭部中插入全局事務(wù) ID,依次傳遞到下游服務(wù)中,從而保證全局事務(wù)的統(tǒng)一提交和回滾操作。

一、背景介紹

在上篇文章中,我們對 Seata 的架構(gòu)設(shè)計(jì)、部署方式以及使用操作做了一個簡單的介紹,相信大家對它已經(jīng)有了初步的了解。

我們知道,在現(xiàn)有的 Spring Cloud 體系中,有兩種技術(shù)方式可以實(shí)現(xiàn)服務(wù)的遠(yuǎn)程調(diào)用。

  • 方式一:通過 Http 工具向目標(biāo)服務(wù)接口發(fā)起遠(yuǎn)程調(diào)用,比如OpenFeign、Http Client等工具。
  • 方式二:通過 Dubbo 工具向目標(biāo)服務(wù)接口發(fā)起遠(yuǎn)程調(diào)用,由于 Dubbo 采用 TCP 協(xié)議進(jìn)行通信,相對 HTTP 方式來說,通信效率會更高一些,應(yīng)用也更廣泛

由于國內(nèi)很多的項(xiàng)目采用 Dubbo 來實(shí)現(xiàn)服務(wù)的遠(yuǎn)程調(diào)用,下面我們以此為例,詳細(xì)的介紹一下如何將 Dubbo 服務(wù)接入 Seata 來實(shí)現(xiàn)分布式事務(wù)操作。

二、方案實(shí)踐

我們以之前的工程為例,對其進(jìn)行適度改造,改造后服務(wù)之間的交互流程可以用如下圖來簡要概括。

具體的實(shí)施過程如下。

2.1、創(chuàng)建服務(wù)接口

首先,創(chuàng)建一個簡單的 Maven 工程,命名為seata-dubbo-api,將需要對外暴露的服務(wù)接口寫入到這里。示例接口如下:

public interface StockApi {

    /**
     * 庫存扣減
     * @param productCode
     * @param count
     * @return
     */
    boolean deduct(String productCode, int count);
}

服務(wù)接口創(chuàng)建完成之后,接下來我們再來創(chuàng)建庫存服務(wù)和訂單服務(wù)。

2.2、創(chuàng)建庫存服務(wù)

然后,建一個 Spring Boot 工程,命名為seata-dubbo-stock,并在pom.xml中引入相關(guān)的依賴內(nèi)容,示例如下:

<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <spring-boot.version>2.2.5.RELEASE</spring-boot.version>
    <spring-cloud.version>Hoxton.SR3</spring-cloud.version>
    <spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
</properties>

<dependencies>
    <!-- SpringBoot web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--mysql 驅(qū)動-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.0.0</version>
    </dependency>
    <!-- Nacos 服務(wù)發(fā)現(xiàn) -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!-- Dubbo -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-dubbo</artifactId>
    </dependency>
    <!-- seata 分布式事務(wù)組件 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
    </dependency>
    <!-- 關(guān)聯(lián)構(gòu)建的api包 -->
    <dependency>
        <groupId>com.example</groupId>
        <artifactId>seata-dubbo-api</artifactId>
        <version>3.0-SNAPSHOT</version>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <!-- 引入 springBoot 版本號 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!-- 引入 spring cloud 版本號 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!-- 引入 spring cloud alibaba 適配的版本號 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

接著,創(chuàng)建一個application.properties文件并配置相關(guān)配置項(xiàng),示例如下:

spring.application.name=seata-dubbo-stock
server.port=9002

# 添加數(shù)據(jù)源配置
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/seata-stock
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

# 配置mybatis全局配置文件掃描
mybatis.config-locatinotallow=classpath:mybatis/mybatis-config.xml
# 配置mybatis的xml配置文件掃描目錄
mybatis.mapper-locatinotallow=classpath:mybatis/mapper/*.xml

# 設(shè)置Nacos的服務(wù)地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

# 指定 Dubbo 服務(wù)實(shí)現(xiàn)類的掃描基準(zhǔn)包
dubbo.scan.base-packages=com.example.cloud.nacos.dubbo.seata
# 指定 Dubbo 服務(wù)暴露的協(xié)議
dubbo.protocol.name=dubbo
# 指定 Dubbo 服務(wù)協(xié)議端口,-1 表示自增端口,從 20880 開始
dubbo.protocol.port=-1
# 指定 Dubbo 服務(wù)注冊中心
dubbo.registry.address=nacos://${spring.cloud.nacos.discovery.server-addr}

# 添加Seata 配置項(xiàng)
# Seata 應(yīng)用編號,默認(rèn)為spring.application.name
seata.application-id=seata-dubbo-stock
# Seata 事務(wù)組編號,用于 TC 集群名
seata.tx-service-group=my_test_tx_group
# Seata 服務(wù)配置項(xiàng),配置對應(yīng)的虛擬組和分組的映射,其中127.0.0.1:8091為 seata 服務(wù)端的監(jiān)聽端口
seata.service.vgroup-mapping.my_test_tx_group=default
seata.service.grouplist.default=127.0.0.1:8091

再然后,創(chuàng)建一個 Dubbo 服務(wù)并實(shí)現(xiàn)上文創(chuàng)建的服務(wù)接口,示例如下:

@com.alibaba.dubbo.config.annotation.Service
publicclass StockApiImpl implements StockApi {

    @Autowired
    private StockService stockService;

    @Override
    public boolean deduct(String productCode, int count) {
        try {
            stockService.deduct(productCode, count);
            // 正??鄢龓齑妫祷?true
            returntrue;
        } catch (Exception e) {
            // 失敗扣除庫存,返回 false
            returnfalse;
        }
    }
}

其中service和mapper層代碼和之前的庫存服務(wù)工程一樣,在此就不再重復(fù)粘貼了。

最后,創(chuàng)建一個服務(wù)啟動類并添加@EnableDiscoveryClient注解,以便將服務(wù)注冊到 Nacos。

@EnableDiscoveryClient
@MapperScan("com.example.cloud.nacos.dubbo.seata")
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

將服務(wù)啟動起來,在瀏覽器中訪問http://127.0.0.1:8848/nacos,如果不出意外的話,在 Nacos 服務(wù)列表可以看到注冊的 dubbo 服務(wù)。

2.3、創(chuàng)建訂單服務(wù)

訂單服務(wù)的創(chuàng)建過程與上文類似。

創(chuàng)建一個 Spring Boot 工程,命名為seata-dubbo-order,其pom.xml所需要的依賴內(nèi)容和服務(wù)啟動類,與上文完全一致,在此就不重復(fù)粘貼了。

其中的web、service和mapper層代碼和之前的訂單服務(wù)工程也完全一致,在此就不再重復(fù)粘貼了。

下面,我們重點(diǎn)對OrderService服務(wù)進(jìn)行改造,將通過 HTTP 工具調(diào)用遠(yuǎn)程服務(wù)接口的邏輯移除,改成用 Dubbo 方式實(shí)現(xiàn)服務(wù)的遠(yuǎn)程調(diào)用,示例如下:

@Component
publicclass OrderService {

    @Autowired
    private OrderMapper orderMapper;


    @com.alibaba.dubbo.config.annotation.Reference
    private StockApi stockApi;

    @GlobalTransactional
    public void create(String userId, String productCode, int orderCount) throws Exception {
        // 通過dubbo服務(wù),實(shí)現(xiàn)遠(yuǎn)程扣減庫存
        stockApi.deduct(productCode, orderCount);

        Order order = new Order();
        order.setUserId(userId);
        order.setProductCode(productCode);
        order.setCount(orderCount);
        order.setMoney(orderCount * 100);
        // 創(chuàng)建訂單
        orderMapper.insert(order);
    }
}

與上文類似,在application.properties配置文件中添加相關(guān)的配置項(xiàng),示例如下:

spring.application.name=seata-dubbo-order
server.port=9001

# 添加數(shù)據(jù)源配置
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/seata-stock
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

# 配置mybatis全局配置文件掃描
mybatis.config-locatinotallow=classpath:mybatis/mybatis-config.xml
# 配置mybatis的xml配置文件掃描目錄
mybatis.mapper-locatinotallow=classpath:mybatis/mapper/*.xml

# 設(shè)置Nacos的服務(wù)地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

# 指定 Dubbo 服務(wù)實(shí)現(xiàn)類的掃描基準(zhǔn)包
dubbo.scan.base-packages=com.example.cloud.nacos.dubbo.seata
# 指定 Dubbo 服務(wù)暴露的協(xié)議
dubbo.protocol.name=dubbo
# 指定 Dubbo 服務(wù)協(xié)議端口,-1 表示自增端口,從 20880 開始
dubbo.protocol.port=-1
# 指定 Dubbo 服務(wù)注冊中心
dubbo.registry.address=nacos://${spring.cloud.nacos.discovery.server-addr}
# 關(guān)閉dubbo客戶端服務(wù)有效性檢查
dubbo.consumer.check=false

# 添加Seata 配置項(xiàng)
# Seata 應(yīng)用編號,默認(rèn)為spring.application.name
seata.application-id=seata-dubbo-order
# Seata 事務(wù)組編號,用于 TC 集群名
seata.tx-service-group=my_test_tx_group
# Seata 服務(wù)配置項(xiàng),配置對應(yīng)的虛擬組和分組的映射,其中127.0.0.1:8091為 seata 服務(wù)端的監(jiān)聽端口
seata.service.vgroup-mapping.my_test_tx_group=default
seata.service.grouplist.default=127.0.0.1:8091

將服務(wù)啟動,再次訪問http://127.0.0.1:8848/nacos的服務(wù)列表,可以看到seata-dubbo-order也成功注冊到服務(wù)中心,界面如下。

2.4、服務(wù)測試

最后,我們還是一起來驗(yàn)證一下如下兩種情況,看看是否能如期實(shí)現(xiàn)。

分布式事務(wù)正常提交

分布式事務(wù)異常回滾

2.4.1、分布式事務(wù)正常提交

首先,重新初始化數(shù)據(jù)庫,數(shù)據(jù)庫中原始數(shù)據(jù)情況如下。

  • seata-stock庫中的庫存數(shù)據(jù)

  • seata-order庫中的訂單數(shù)據(jù)

接著,在瀏覽器中訪問http://127.0.0.1:9001/order/create?userId=張三&productCode=wahaha&orderCount=1,它會執(zhí)行如下兩個動作:

  • 第一個:調(diào)用庫存服務(wù),將產(chǎn)品產(chǎn)品編碼為wahaha的庫存減 1;
  • 第二個:如果庫存扣減成功,插入一條產(chǎn)品編碼為wahaha數(shù)量為 1 的訂單信息;

發(fā)起接口請求后,再次回看數(shù)據(jù)庫,看看目標(biāo)數(shù)據(jù)表中的數(shù)據(jù)情況。

  • seata-stock庫中的庫存數(shù)據(jù)

  • seata-order庫中的訂單數(shù)據(jù)

從數(shù)據(jù)結(jié)果來看,與預(yù)期一致。

我們還可以通過查看服務(wù)的日志信息,來觀察分支事務(wù)的操作情況。

其中Branch commit result信息代表分支事務(wù)的二階段操作。

2.4.2、分布式事務(wù)異?;貪L

測試完正常流程之后,下面我們再來驗(yàn)證一下異常流程。

修改OrderService類中create()方法代碼,在創(chuàng)建訂單完成之后,試圖拋出異常,測試一下扣減的庫存數(shù)據(jù)是否能正?;貪L。

首先,我們還是對數(shù)據(jù)庫中原始數(shù)據(jù)進(jìn)行截個圖。

  • seata-stock庫中的庫存數(shù)據(jù)

  • seata-order庫中的訂單數(shù)據(jù)

然后,再次在瀏覽器中訪問http://127.0.0.1:9001/order/create?userId=張三&productCode=wahaha&orderCount=1。

預(yù)期的結(jié)果是:兩個庫的數(shù)據(jù)應(yīng)該都不會發(fā)生變化!

再次回看數(shù)據(jù)庫,觀察目標(biāo)數(shù)據(jù)表中的數(shù)據(jù)情況。

  • seata-stock庫中的庫存數(shù)據(jù)

  • seata-order庫中的訂單數(shù)據(jù)

數(shù)據(jù)在5秒之內(nèi)是執(zhí)行成功的,為了便于觀察數(shù)據(jù)變化,我們在上文拋異常的位置停頓了 5 秒。

過 5 秒后,再次回看數(shù)據(jù)庫表中的數(shù)據(jù)情況,結(jié)果如下。

  • seata-stock庫中的庫存數(shù)據(jù)

  • seata-order庫中的訂單數(shù)據(jù)

從數(shù)據(jù)最終結(jié)果來看,與預(yù)期是一致的。

在瀏覽器中訪問http://127.0.0.1:7091,登陸 Seata TC Server 服務(wù)監(jiān)控臺,還可以看到全局事務(wù)的注冊信息和狀態(tài)。

三、Seata 服務(wù)地址配置化

隨著 Seata 的集群部署數(shù)量的增加,微服務(wù)中的Seata 服務(wù)地址配置可能會越來越臃腫,此時我們可能希望借助服務(wù)注冊中心來加載 Seata TC Server 的地址,這個時候如何實(shí)現(xiàn)呢?

正如之前我們所介紹的,Seata TC Server 對主流的注冊中心也提供了集成,Seata 客戶端可以通過注冊中心獲取 Seata TC Server 所在的服務(wù)實(shí)例。

引入注冊中心之后,Seata 的交互流程可以用如下圖來概括。

下面我們以服務(wù)注冊中心 Nacos 為例,簡單的介紹一下它的配置方式。

3.1、Seata 服務(wù)端配置方式

打開 Seata 安裝包中conf/application.example.yml文件,找到store.registry相關(guān)配置屬性。

將其復(fù)制出來,然后拷貝到conf/application.yml文件中。

最后,重啟 Seata TC Server 服務(wù)即可。

訪問 nacos 的服務(wù)控制臺,如果看到 Seata 服務(wù),說明服務(wù)注冊成功了。

3.2、Seata 客戶端端配置方式

以seata-dubbo-order服務(wù)為例,修改application.properties配置文件中seata相關(guān)的配置項(xiàng),示例如下:

# Seata 應(yīng)用編號,默認(rèn)為spring.application.name
seata.application-id=seata-dubbo-stock
# Seata 事務(wù)組編號,用于 TC 集群名
seata.tx-service-group=my_test_tx_group
# Seata 服務(wù)配置項(xiàng),配置對應(yīng)的虛擬組和分組的映射,此處必須填寫default
seata.service.vgroup-mapping.my_test_tx_group=default
# 設(shè)置 seata 注冊中心類型為nacos,默認(rèn)為 file
seata.registry.type=nacos
# 設(shè)置 seata 服務(wù)端中配置 nacos 相關(guān)信息
seata.registry.nacos.applicatinotallow=seata-server
seata.registry.nacos.server-addr=127.0.0.1:8848
seata.registry.nacos.group=SEATA_GROUP

此處的調(diào)整主要是增加 seata 的注冊中心配置,客戶端通過配置的注冊中心來獲取 Seata TC Server 服務(wù)實(shí)例地址。

最后將相關(guān)的服務(wù)進(jìn)行重啟,再次在瀏覽器中訪問http://127.0.0.1:9001/order/create?userId=張三&productCode=wahaha&orderCount=1。

不出意外的話,數(shù)據(jù)測試結(jié)果與上文一致。

3.3、錯誤排查

如果測試中遇到類似如下異常信息。

這種情況通常是 seata 客戶端版本與服務(wù)端版本不兼容導(dǎo)致的,可以嘗試升級 seata 客戶端版本,以便與 seata 服務(wù)端進(jìn)行適配。

以本文工程為例,Seata TC Server 服務(wù)端采用的1.5.2版本,而 Seata 客戶端采用的是1.1.0版本,可見兩者版本相差太大,當(dāng)發(fā)起接口請求時就出現(xiàn)了上文的錯誤信息。

通過查閱版本號適配情況,Seata 客戶端的1.3.0版本可以與 Seata 服務(wù)端進(jìn)行兼容,因此可以直接升級spring-cloud-alibaba的版本號,示例如下:

<!--原來是 2.2.1.RELEASE版本,將其升級為 2.2.3.RELEASE-->
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>

由于spring-cloud-alibaba的2.2.3.RELEASE版本中集成的seata客戶端版本號為1.3.0,當(dāng)重啟服務(wù)再次發(fā)起接口請求時,一切恢復(fù)正常。

因此當(dāng)代碼和配置都沒有問題時,服務(wù)無法啟動或者運(yùn)行錯誤,通常情況與版本號有很大的關(guān)系。可以檢查一下工程中的版本號與官方要求的版本號是否出現(xiàn)不兼容現(xiàn)象。

四、小結(jié)

最后總結(jié)一下,本文主要圍繞 dubbo 整合 seata 實(shí)現(xiàn)服務(wù)分布式事務(wù)操作做了一次知識內(nèi)容的總結(jié)和整理,內(nèi)容比較多,如果有描述不對的地方,歡迎大家留言指出。

如果當(dāng)前的服務(wù)工程采用的是 openFeign 來實(shí)現(xiàn)服務(wù)遠(yuǎn)程調(diào)用,也可以通過集成spring-cloud-starter-alibaba-seata依賴包實(shí)現(xiàn)分布式事務(wù)操作,其實(shí)現(xiàn)原理也是在遠(yuǎn)程調(diào)用的請求頭部中插入全局事務(wù) ID,依次傳遞到下游服務(wù)中,從而保證全局事務(wù)的統(tǒng)一提交和回滾操作。

五、參考

1、https://seata.apache.org/zh-cn/docs/overview/what-is-seata/

2、https://www.iocoder.cn/Seata/install/

3、https://www.iocoder.cn/Spring-Boot/Seata/

責(zé)任編輯:武曉燕 來源: 潘志的技術(shù)筆記
相關(guān)推薦

2024-06-04 10:58:30

2022-06-27 08:21:05

Seata分布式事務(wù)微服務(wù)

2022-06-21 08:27:22

Seata分布式事務(wù)

2024-05-11 07:37:43

數(shù)據(jù)Redis策略

2022-03-24 07:51:27

seata分布式事務(wù)Java

2023-07-25 09:52:00

本地事務(wù)宕機(jī)

2024-08-19 09:05:00

Seata分布式事務(wù)

2017-06-20 09:42:52

網(wǎng)絡(luò)安全法數(shù)據(jù)隱私法網(wǎng)絡(luò)安全

2022-07-10 20:24:48

Seata分布式事務(wù)

2022-06-27 08:36:27

分布式事務(wù)XA規(guī)范

2024-06-06 08:40:07

2021-11-22 16:30:30

分布式一致性分布式系統(tǒng)

2021-08-06 08:33:27

Springboot分布式Seata

2025-05-09 01:04:00

2018-07-15 08:18:44

緩存數(shù)據(jù)庫數(shù)據(jù)

2021-06-16 08:33:02

分布式事務(wù)ACID

2025-06-09 08:00:37

分布式文件系統(tǒng)

2025-04-03 09:51:37

2025-04-30 10:44:02

2024-10-09 14:14:07

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 日韩有码一区二区三区 | 久久国产精品视频 | www.日韩在线 | 日韩欧美国产一区二区 | 欧美视频在线播放 | 日本一区二区三区四区 | 午夜色婷婷| 欧美激情视频网站 | 一级在线观看 | 日韩成人免费av | 在线不卡视频 | 成人av网站在线观看 | 国内精品久久久久久久 | 日日操夜夜操天天操 | 91精品国产91久久综合桃花 | 国产美女免费视频 | 亚洲国产日韩一区 | 欧美精品在线一区二区三区 | 激情福利视频 | 亚洲一区不卡 | 91在线电影 | 国产欧美一区二区三区久久手机版 | 久久久91 | 国产98色在线 | 日韩 | www.国产 | 欧美色综合 | 欧美日韩久久精品 | 欧美综合国产精品久久丁香 | 国产精品99久久久久久久久久久久 | 又黑又粗又长的欧美一区 | 欧美a区 | 成人1区2区| 久久天天 | 国产剧情一区 | 久久99精品久久久水蜜桃 | 国产高清久久 | 欧美成人免费 | 网站黄色在线免费观看 | 色综合av | 成人在线观看黄 | 99久久99|