最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

前言

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

下訂單的例子

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

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

@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) {
        // ...
    }
}

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

這么寫,會(huì)遇到什么問(wèn)題呢?

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

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

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

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

有沒有更好的方法呢?

我們可以訂單表所在的同一數(shù)據(jù)庫(kù)中有一個(gè)表“發(fā)件箱”(在最簡(jiǎn)單的情況下,它可以有一個(gè)列“消息”和當(dāng)前時(shí)間戳)。保存訂單時(shí),在同一個(gè)事務(wù)中,我們?cè)?ldquo;發(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) {
        // ...
    }
}

可以看到,我們?cè)谝淮问聞?wù)中將訂單和發(fā)件箱實(shí)體保存在我們的數(shù)據(jù)庫(kù)中。然后我們發(fā)送一條消息,如果成功,我們刪除這條消息。

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

它可以使用將在后臺(tái)運(yùn)行的計(jì)劃任務(wù)來(lái)完成,該任務(wù)將嘗試發(fā)送在表發(fā)件箱中顯示超過(guò) 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 秒運(yùn)行一個(gè)任務(wù),并發(fā)送之前沒有發(fā)送過(guò)的消息。如果消息成功發(fā)送到消息隊(duì)列,但發(fā)件箱實(shí)體沒有從數(shù)據(jù)庫(kù)中刪除(例如因?yàn)閿?shù)據(jù)庫(kù)問(wèn)題),那么下次該后臺(tái)任務(wù)將嘗試再次將此消息發(fā)送到消息隊(duì)列。但這也意味著我們消息的消費(fèi)者必須做好冪等處理,因?yàn)榭赡軙?huì)多次接收相同的消息。

發(fā)件箱模式

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

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

總結(jié)

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

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

相關(guān)文章

  • Tomcat+Eclipse亂碼問(wèn)題解決方法與步驟

    Tomcat+Eclipse亂碼問(wèn)題解決方法與步驟

    亂碼問(wèn)題是大家在日常開發(fā)過(guò)程中經(jīng)常會(huì)遇到的問(wèn)題,由于各自環(huán)境的不同,解決起來(lái)也費(fèi)時(shí)費(fèi)力,本文主要介紹一般性亂碼問(wèn)題的解決方法與步驟,開發(fā)工具采用Eclipse+Tomcat,統(tǒng)一設(shè)置項(xiàng)目編碼UTF-8為例,感興趣的朋友跟隨小編一起看看吧
    2023-08-08
  • Javabean簡(jiǎn)介_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Javabean簡(jiǎn)介_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要介紹了Javabean簡(jiǎn)介,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-07-07
  • 基于java開發(fā)之系統(tǒng)托盤的應(yīng)用

    基于java開發(fā)之系統(tǒng)托盤的應(yīng)用

    本篇文章介紹了,基于java開發(fā)之系統(tǒng)托盤的應(yīng)用。需要的朋友參考下
    2013-05-05
  • Java實(shí)現(xiàn)讀取163郵箱,qq郵箱的郵件內(nèi)容

    Java實(shí)現(xiàn)讀取163郵箱,qq郵箱的郵件內(nèi)容

    這篇文章主要利用Java語(yǔ)言實(shí)現(xiàn)讀取163郵箱和qq郵箱的郵件內(nèi)容,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起動(dòng)手試一試
    2022-02-02
  • Java使用OCR技術(shù)識(shí)別驗(yàn)證碼實(shí)現(xiàn)自動(dòng)化登陸方法

    Java使用OCR技術(shù)識(shí)別驗(yàn)證碼實(shí)現(xiàn)自動(dòng)化登陸方法

    在本篇文章里小編給大家分享的是關(guān)于Java 如何使用 OCR 技術(shù)識(shí)別驗(yàn)證碼實(shí)現(xiàn)自動(dòng)化登陸的相關(guān)知識(shí)點(diǎn)內(nèi)容,需要的朋友們學(xué)習(xí)下。
    2019-08-08
  • Intellij Mybatis連接Mysql數(shù)據(jù)庫(kù)

    Intellij Mybatis連接Mysql數(shù)據(jù)庫(kù)

    最近在搞android的項(xiàng)目,在開發(fā)過(guò)程中遇到了好多問(wèn)題,今天小編給大家說(shuō)下mybatis連接MySQL數(shù)據(jù)庫(kù)的方法,感興趣的朋友跟著小編一起學(xué)習(xí)吧
    2016-10-10
  • 關(guān)于Java中Bean的生命周期詳解

    關(guān)于Java中Bean的生命周期詳解

    這篇文章主要介紹了關(guān)于Java中Bean的生命周期詳解,所謂的?命周期指的是?個(gè)對(duì)象從誕?到銷毀的整個(gè)?命過(guò)程,我們把這個(gè)過(guò)程就叫做?個(gè)對(duì)象的?命周期,需要的朋友可以參考下
    2023-08-08
  • 深入理解Java序列化與反序列化

    深入理解Java序列化與反序列化

    今天教大家深入理解Java的序列化與反序列化,文中介紹的非常詳細(xì),有很多代碼示例,對(duì)正在學(xué)習(xí)Java的小伙伴們很有幫助,需要的朋友可以參考下
    2021-05-05
  • springboot框架阿里開源低代碼工具LowCodeEngine

    springboot框架阿里開源低代碼工具LowCodeEngine

    這篇文章主要為大家介紹了springboot框架阿里開源低代碼LowCodeEngine工具使用詳解有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • mybatis相同的sql查詢第二次查不出結(jié)果問(wèn)題

    mybatis相同的sql查詢第二次查不出結(jié)果問(wèn)題

    這篇文章主要介紹了mybatis相同的sql查詢第二次查不出結(jié)果問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01

最新評(píng)論

滨海县| 洛川县| 平山县| 安岳县| 石嘴山市| 怀来县| 靖边县| 祁阳县| 常德市| 娄烦县| 海晏县| 安宁市| 来安县| 蓝山县| 泸定县| 苏尼特左旗| 碌曲县| 农安县| 贵阳市| 贵定县| 彰化市| 潮安县| 抚宁县| 渑池县| 中宁县| 会泽县| 内江市| 承德县| 疏勒县| 许昌市| 临清市| 宣汉县| 西平县| 同江市| 北辰区| 志丹县| 宁明县| 东辽县| 棋牌| 朝阳县| 溧阳市|