交貨處理可能不需要順序,因為訊息順序處理和伸縮性是很難平衡的 - particular
嘗試應用嚴格的有序處理將對我們的系統施加人為限制。這是因為保證訊息排序在技術上非常困難,即使成功,也總是需要權衡諸如較低的訊息吞吐量和較低的可擴充套件性,這會妨礙系統成功的能力。
比如比薩店交付披薩時,不一定要根據訂單前後順序準備披薩,而是根據比薩的製作順序,有些披薩需要燒烤時間長一些,有一些需要短一點。
讓我們來看看為什麼從技術角度來保證有順序的交貨是多麼困難。
異常
在訊息處理程式碼中程式設計異常時會發生什麼?即使使用最強大的程式碼,異常仍然可能發生。作為開發人員,這對我們來說並不陌生。畢竟,這就是我們編寫單元測試的原因:防範意外。但並非一切都在我們的控制之下,而且很多都可能出錯。
我們可以使用因瞬態錯誤而丟擲異常的訊息。雖然並非所有錯誤都是嚴重的,但我們確實需要處理這個問題。很多時候我們可以簡單地重新排列失敗的訊息來解決問題。但是我們也應該能夠處理“毒藥”訊息,這些訊息會一直失敗,應暫時放在一邊,以便稍後重試。
我們是在幾秒鐘內還是幾天後重試一次有毒資訊是無關緊要的。重要的一點是,毒藥訊息已被處理,並且被重試後會從佇列中取走。結果是我們預期按順序到達的訊息現在正在按順序處理。
可伸縮性
當系統以訊息傳遞作為其基礎之一構建時,向外擴充套件伸縮的能力是無法按照事前預測計劃進行的,不幸的是,這使得有序交付幾乎無法支援。
向外擴充套件伸縮的能力是一個非常強大的功能。您可以通過讓更多伺服器處理訊息,無需購買功能更強大且更昂貴的硬體。每個伺服器基本上都在競爭訊息,儘可能多地處理訊息。回到我們的披薩店,這類似於購買更多的烤箱,一次烘焙更多的比薩餅,而不是升級現有的烤箱,以更快地處理相同數量的比薩餅。
雖然伺服器可以擴充套件,但它們彼此不瞭解。這通常不是問題,除了有序地交貨。需要按順序到達的訊息可能會在不同的計算機上處理。其中一臺機器可以更快地完成工作或完成工作,從而導致訊息無序處理。即使您沒有伸縮並且只有一臺伺服器,您的伺服器也必須使用單個執行緒處理訊息才能保證有順序(因此速度較慢),兩者類似。
回到現實世界
當物質世界中的事情發生故障時,事情通常會通過各種檢查和平衡來解決。我可能先在其他人前面點了我的披薩,但如果他們的披薩先做好,他們會先拿到他們的披薩。
有時我會在回家的路上訂一塊比薩餅。當我下訂單時,商店將告訴我大約需要多長時間。如果我早點到達那裡,我會支付它並等到它完成。但也許我的交通延遲或等到我正在第十次觀看的權力的遊戲節目結束。當我到達那裡時,我的披薩正等著我,所以我付錢給它並隨身攜帶。
這種情況的重要部分是兩個事件 : “披薩準備好了”與”披薩付款” ,可能發生故障。但兩者都需要在比薩餅交付之前完成。在系統建模術語中,我們可能有一個DeliveryService,它依賴於來自PaymentService的OrderPaid訊息和來自KitchenService的PizzaPrepared訊息。如果首先獲得OrderPaid訊息,則它無法交付,因為它不知道它的訂單中的披薩是否已經準備好。在這種情況下,您可以想象客戶會定期詢問DeliveryService(即收銀員),看看他的披薩是否已經完成。
在軟體中建模無序訊息
相反,我們可以使用名為sagas的NServiceBus功能。這些是訊息驅動的狀態機,允許我們協調業務流程。Sagas自動儲存狀態,處理併發,並可以幫助我們協調長期執行的業務流程。
讓我們來看看NServiceBus中的一個Saga如何處理無序到達的訊息。通過一個小狀態,它可以記住已經發生的事情並根據這些約束採取行動。為了簡化程式碼,我們將使用兩個標誌。
class DeliveryPolicy : Saga { public Handle(OrderPaid message) { Data.OrderPaid = true; VerifyIfPizzaCanBeDelivered(); } public Handle(PizzaPrepared message) { Data.PizzaPrepared = true; VerifyIfPizzaCanBeDelivered(); } private VerifyIfPizzaCanBeDelivered() { if (Data.OrderPaid && Data.PizzaPrepared) { // ... send message that pizza can be delivered } } } |
在此示例中,當任一訊息到達時,Saga的狀態會發生變化。然後檢查此狀態以檢視是否應繼續交付。我們假設PizzaPrepared訊息首先到達。它會將訂單標記為已準備好,然後檢查是否已滿足所有條件;如果還沒有準備好,Saga回到等待holding模式,直到OrderPaid訊息到來。此時,VerifyIfPizzaCanBeDelivered確定已滿足所有條件,我們可以繼續完成訂單。
但如果OrderPaid訊息首先到達怎麼辦?也許KitchenService複製了訂單但沒有及時完成。在這種情況下,Saga也幾乎做完全相同事情,它將訂單標記為已付款,然後檢查其內部狀態以檢視是否已滿足所有條件以繼續。如果還沒有,訂單一直等待holding到PizzaPrepared事件到來並完成提供披薩的要求。
Sagas提供了一個解決所有這些排序問題的工具,並提供了橫向擴充套件和樂觀併發等所有技術考慮因素,使您可以專注於業務需求。
總結
從技術角度來看,有順序交貨、處理錯誤以及擁有可擴充套件的系統三者具備幾乎是不可能的。同時,業務流程實際上不太可能需要有序交付。從業務和技術角度來看,我們都需要能夠適應不同的場景。我們實際需要的是一種處理這些替代方案的方法。
但是訊息將無序到達,應該允許它們無序到達。我們可以接受它,而不是試圖“修復”這個問題,而是提出問題並提供替代流程。我們不應該接受一種技術限制,迫使客戶等待,而他們的披薩變冷,只是因為其他客戶先訂購。
相關文章
- PrepareStatement物件進行批處理的典型步驟順序REST物件
- 實際業務處理 Kafka 訊息丟失、重複消費和順序消費的問題Kafka
- 如何保證訊息佇列的順序性?佇列
- 關於RocketMQ的順序訊息MQ
- 分散式訊息佇列:如何保證訊息的順序性分散式佇列
- 解析 RocketMQ 業務訊息--“順序訊息”MQ
- Kafka 如何保證訊息消費的全域性順序性Kafka
- RocketMq如何順序消費的訊息offestMQ
- HTML 屬性順序HTML
- MQ收到無序的訊息時如何進行業務處理MQ行業
- RabbitMQ多消費者順序性消費訊息實現MQ
- RMQ——支援合併和優先順序的訊息佇列MQ佇列
- Android學習 —— 測試init.rc中的條件觸發的處理順序Android
- CSS 屬性宣告順序CSS
- html優先順序和層疊性HTML
- 如何使用Rust的gaffer實現優先順序的微批處理排程器 - njkRust
- python運算子及優先順序順序Python
- 牛逼的產品經理是如何為試驗想法排列優先順序的?
- 順序表
- 第2章 順序表及其順序儲存
- Qt 大小端位元組序的處理QT
- DNS解析順序是怎樣的?DNS
- JS控制音訊順序播放JS音訊
- 線性表的使用——順序實現
- 佇列順序性引發的思考佇列
- SpringBoot配置檔案優先順序載入順序Spring Boot
- 順序表應用5:有序順序表歸併
- 順序表應用6:有序順序表查詢
- 美團二面:SpringBoot讀取配置優先順序順序是什麼?Spring Boot
- JavaScript遍歷物件屬性順序JavaScript物件
- CSS font 複合屬性順序CSS
- UML順序圖
- 順序查詢
- 順序結構
- 順序刷題
- CMake和靜態庫順序
- 線性表的順序儲存-順序表,對“突然的自我”的否定,對自我的揚棄
- 如何保持json序列化的順序性?JSON