欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文帶你了解微服務(wù)架構(gòu)中的"發(fā)件箱模式"

 更新時間:2023年01月09日 08:24:50   作者:JAVA旭陽  
微服務(wù)架構(gòu)如今非常的流行,這個架構(gòu)下可能經(jīng)常會遇到“雙寫”的場景。本文就和大家分享一個“發(fā)件箱模式”,?感興趣的小伙伴可以了解一下

前言

微服務(wù)架構(gòu)如今非常的流行,這個架構(gòu)下可能經(jīng)常會遇到“雙寫”的場景。雙寫是指您的應(yīng)用程序需要在兩個不同的系統(tǒng)中更改數(shù)據(jù)的情況,比如它需要將數(shù)據(jù)存儲在數(shù)據(jù)庫中并向消息隊列發(fā)送事件。您需要保證這兩個操作都會成功。如果兩個操作之一失敗,您的系統(tǒng)可能會變得不一致。那針對這樣的情況有什么好的方法或者設(shè)計保證呢?本文就和大家分享一個“發(fā)件箱模式”, 可以很好的避免此類問題。

下訂單的例子

假設(shè)我們有一個 OrderService 類,它在創(chuàng)建新訂單時被調(diào)用,此時它應(yīng)該將訂單實體保存在數(shù)據(jù)庫中并向交付微服務(wù)發(fā)送一個事件,以便交付部門可以開始計劃交付。

你的代碼可能是下面這樣子的:

@Service
public record OrderService(
    IDeliveryMessageQueueService deliveryMessageQueueService,
    IOrderRepository orderRepository,
    TransactionTemplate transactionTemplate) implements IOrderService {

    @Override
    public void create(int id, String description) {
        String message = buildMessage(id, description);

        transactionTemplate.executeWithoutResult(transactionStatus -> {
            // 保存訂單
            orderRepository.save(id, description);
        });

        // 發(fā)送消息
        deliveryMessageQueueService.send(message);
    }

    private String buildMessage(int id, String description) {
        // ...
    }
}

可以看到我們在事務(wù)中將訂單保存在數(shù)據(jù)庫中,然后我們使用消息隊列將事件發(fā)送到交付服務(wù)。這是雙寫的一個場景。

這么寫,會遇到什么問題呢?

首先,如果我們保存了訂單但是發(fā)送消息失敗了怎么辦?送貨服務(wù)永遠(yuǎn)不會收到消息。

那你可能想到把保存訂單和發(fā)消息放到同一個事務(wù)中不就可以了嗎,就是是將 deliveryMessageQueueService#send 移動到與 orderRepository#save 相同的事務(wù)中,如下圖:

transactionTemplate.executeWithoutResult(transactionStatus -> {
            // 保存訂單
            orderRepository.save(id, description);
            // 發(fā)送消息
        	deliveryMessageQueueService.send(message);
        });

實際上,在數(shù)據(jù)庫事務(wù)內(nèi)部建立 TCP 連接是一種糟糕的做法,我們不應(yīng)該這樣做。

有沒有更好的方法呢?

我們可以訂單表所在的同一數(shù)據(jù)庫中有一個表“發(fā)件箱”(在最簡單的情況下,它可以有一個列“消息”和當(dāng)前時間戳)。保存訂單時,在同一個事務(wù)中,我們在“發(fā)件箱”表中保存了一條消息。消息一發(fā)送,我們就可以將其從發(fā)件箱表中刪除,代碼如下:

@Service
public record OrderService(
    IDeliveryMessageQueueService deliveryMessageQueueService,
    IOrderRepository orderRepository,
    IOutboxRepository outboxRepository,
    TransactionTemplate transactionTemplate) implements IOrderService {

    @Override
    public void create(int id, String description) {
        UUID outboxId = UUID.randomUUID();
        String message = buildMessage(id, description);

        transactionTemplate.executeWithoutResult(transactionStatus -> {
            // 保存訂單
            orderRepository.save(id, description);
            // 保存到發(fā)件箱
            outboxRepository.save(new OutboxEntity(outboxId, message));
        });

        deliveryMessageQueueService.send(message);
        
        // 刪除
        outboxRepository.delete(outboxId);
    }

    private String buildMessage(int id, String description) {
        // ...
    }
}

可以看到,我們在一次事務(wù)中將訂單和發(fā)件箱實體保存在我們的數(shù)據(jù)庫中。然后我們發(fā)送一條消息,如果成功,我們刪除這條消息。

如果 deliveryMessageQueueService#send 失敗會怎樣?(例如,您的應(yīng)用程序被終止或消息隊列或數(shù)據(jù)庫不可用)。在這種情況下,outboxRepository#delete 將不會運行,我們必須重試發(fā)送消息。

它可以使用將在后臺運行的計劃任務(wù)來完成,該任務(wù)將嘗試發(fā)送在表發(fā)件箱中顯示超過 X 秒(例如 10 秒)的消息,如下面的代碼。

@Service
public record OutboxRetryTask(IOutboxRepository outboxRepository,
                              IDeliveryMessageQueueService deliveryMessageQueueService) {

    @Scheduled(fixedDelayString = "10000")
    public void retry() {
        List<OutboxEntity> outboxEntities = outboxRepository.findAllBefore(Instant.now().minusSeconds(60));
        for (OutboxEntity outbox : outboxEntities) {
            deliveryMessageQueueService.send(outbox.message());
            outboxRepository.delete(outbox.id());
        }
    }
}

在這里你可以看到,我們每 10 秒運行一個任務(wù),并發(fā)送之前沒有發(fā)送過的消息。如果消息成功發(fā)送到消息隊列,但發(fā)件箱實體沒有從數(shù)據(jù)庫中刪除(例如因為數(shù)據(jù)庫問題),那么下次該后臺任務(wù)將嘗試再次將此消息發(fā)送到消息隊列。但這也意味著我們消息的消費者必須做好冪等處理,因為可能會多次接收相同的消息。

發(fā)件箱模式

通過上面的例子,我們可以抽象出“發(fā)件箱模式”。

  • 在數(shù)據(jù)庫里面額外增加一個outbox表用于存儲需要發(fā)送的event
  • 把直接發(fā)送event的步驟換成先把event存儲到數(shù)據(jù)庫outbox表
  • 程序啟動一個 job 不斷去抓取 outbox 表里面的記錄,通過推送線程完成不同業(yè)務(wù)的推送
  • 最后刪除發(fā)送成功的記錄
  • 提醒消息消費端要做好冪等處理

總結(jié)

發(fā)件箱模式雖然聽上去可能很簡單,但是在平時開發(fā)中可能會忽略掉。如果還不能理解,我們可以將它類比到生活的場景,寄信人只需要寫好信件,放入收件箱,之后就不用管了。送信的人會來收件箱取走信件,根據(jù)信件里需要送到的地址,將信件送至目的地。這樣做的好處就是,寄信人寫好信之后,就不需要等待收信人有空的時候才能寄信,只需要往發(fā)件箱里丟就好了。

以上就是一文帶你了解微服務(wù)架構(gòu)中的"發(fā)件箱模式"的詳細(xì)內(nèi)容,更多關(guān)于微服務(wù)架構(gòu)發(fā)件箱模式的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 深入理解SpringMVC的參數(shù)綁定與數(shù)據(jù)響應(yīng)機制

    深入理解SpringMVC的參數(shù)綁定與數(shù)據(jù)響應(yīng)機制

    本文將深入探討SpringMVC的參數(shù)綁定方式,包括基本類型、對象、集合等類型的綁定方式,以及如何處理參數(shù)校驗和異常。同時,本文還將介紹SpringMVC的數(shù)據(jù)響應(yīng)機制,包括如何返回JSON、XML等格式的數(shù)據(jù),以及如何處理文件上傳和下載。
    2023-06-06
  • java導(dǎo)出數(shù)據(jù)庫的全部表到excel

    java導(dǎo)出數(shù)據(jù)庫的全部表到excel

    這篇文章主要為大家詳細(xì)介紹了java導(dǎo)出數(shù)據(jù)庫的全部表到excel的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-03-03
  • Java中泛型的使用和優(yōu)點解析

    Java中泛型的使用和優(yōu)點解析

    這篇文章主要介紹了Java中泛型的使用和優(yōu)點解析,泛型使用過程中,操作的數(shù)據(jù)類型被指定為一個參數(shù),這種參數(shù)類型可以用在類、接口和方法中,分別被稱為泛型類、泛型接口、泛型方法,需要的朋友可以參考下
    2023-09-09
  • 全面總結(jié)java IO體系

    全面總結(jié)java IO體系

    這篇文章主要介紹了java IO體系的全面總結(jié),下面和小編來一起學(xué)習(xí)一下吧
    2019-05-05
  • java校驗json的格式是否符合要求的操作方法

    java校驗json的格式是否符合要求的操作方法

    在日常開發(fā)過程中,會有這樣的需求,校驗?zāi)硞€json是否是我們想要的數(shù)據(jù)格式,這篇文章主要介紹了java校驗json的格式是否符合要求,需要的朋友可以參考下
    2023-04-04
  • FeignClient實現(xiàn)接口調(diào)用方式(不同參數(shù)形式)

    FeignClient實現(xiàn)接口調(diào)用方式(不同參數(shù)形式)

    這篇文章主要介紹了FeignClient實現(xiàn)接口調(diào)用方式(不同參數(shù)形式),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java大文件上傳詳解及實例代碼

    Java大文件上傳詳解及實例代碼

    這篇文章主要介紹了Java大文件上傳詳解及實例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • 詳解Springboot Oauth2 Server搭建Oauth2認(rèn)證服務(wù)

    詳解Springboot Oauth2 Server搭建Oauth2認(rèn)證服務(wù)

    這篇文章主要介紹了Springboot Oauth2 Server 搭建Oauth2認(rèn)證服務(wù),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-05-05
  • java中hashCode方法與equals方法的用法總結(jié)

    java中hashCode方法與equals方法的用法總結(jié)

    總的來說,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。前者集合內(nèi)的元素是有序的,元素可以重復(fù);后者元素?zé)o序,但元素不可重復(fù)
    2013-10-10
  • SpringMVC?RESTFul實戰(zhàn)案例刪除功能實現(xiàn)

    SpringMVC?RESTFul實戰(zhàn)案例刪除功能實現(xiàn)

    這篇文章主要為大家介紹了SpringMVC?RESTFul實戰(zhàn)案例刪除功能實現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05

最新評論