🔥《吊打面試官》系列-分佈式事務、重複消費、順序消費

你知道的越多,你不知道的越多

點贊再看,養成習慣

GitHub上已經開源 https://github.com/JavaFamily 有一線大廠面試點腦圖、個人聯繫方式和人才交流羣,歡迎Star和完善

前言

消息隊列在互聯網技術存儲方面使用如此廣泛,幾乎所有的後端技術面試官都要在消息隊列的使用和原理方面對小夥伴們進行360°的刁難。

作爲一個在互聯網公司面一次拿一次Offer的麪霸,打敗了無數競爭對手,每次都只能看到無數落寞的身影失望的離開,略感愧疚(請允許我使用一下誇張的修辭手法)。

於是在一個寂寞難耐的夜晚,暖男我痛定思痛,決定開始寫《吊打面試官》系列,希望能幫助各位讀者以後面試勢如破竹,對面試官進行360°的反擊,吊打問你的面試官,讓一同面試的同僚瞠目結舌,瘋狂收割大廠Offer!

撈一下

上一期,簡單的介紹了一下消息隊列的基礎知識,裏面有消息隊列的應用場景,以及使用之後可能帶來的問題,但是上期沒對怎麼解決這些問題做回答,因爲要控制篇幅嘛(明明是自己覺得MQ寫不了多少期,要多懟一期出來!渣男)

咳咳,我們言歸正傳,沒看的朋友去看一下,有助於這期的閱讀:

《吊打面試官》系列-消息隊列基礎

面試開始

一個風度翩翩,穿着格子襯衣的中年男子,拿着一個滿是劃痕的mac向你走來,看着錚亮的頭,心想着肯定是尼瑪頂級架構師吧!但是我們看過暖男敖丙的系列,腹有詩書氣自華,虛都不虛。

沒錯小夥子還是我,上次話說一半你就溜了,這次我非得好好的問問你。

好的面試官,因爲上次着急,敖丙的系列更新了所以趕回家去看了!

我信你個鬼,我們開始吧,上次說到了消息隊列的消息重複消費,你能跟我介紹這是怎麼樣子的場景麼?

消息重複消費是使用消息隊列之後,必須考慮的一個問題,也是比較嚴重和常見的問題,帥丙我在開發過程中,但凡用到了消息隊列,我第一時間考慮的就是重複消費的問題。

就比如有這樣的一個場景,用戶下單成功後我需要去一個活動頁面給他加GMV(銷售總額),最後根據他的GMV去給他發獎勵,這是電商活動很常見的玩法。

類似累計下單金額到哪個梯度給你返回什麼梯度的獎勵這樣。

我只能告訴你這樣的活動頁面10000%是用異步去加的(別問我爲什麼,因爲這個活動的後端是敖丙我做的😂),不然你想,你一個用戶下一單就給他加一下,那就意味着對那張表就要操作一下,你考慮下雙十一當天多少次對這個表的操作?這數據庫或者緩存都頂不住吧。

而且大家應該也有這樣的體會,你下單了馬上去看一些活動頁面,有時候馬上就有了,有時候卻延遲有很久,爲啥?這個速度取決於消息隊列的消費速度,消費慢堵塞了就遲點看到唄。

你下個單支付成功你就發個消息出去,我們上面那個活動的開發人員就監聽你的支付成功消息,我監聽到你這個訂單成功支付的消息,那我就去我活動GMV表裏給你加上去,聽到這裏大家可能覺得順理成章

但是我告訴大家一般消息隊列的使用,我們都是有重試機制的,就是說我下游的業務發生異常了,我會拋出異常並且要求你重新發一次

我這個活動這裏發生錯誤,你要求重發肯定沒問題。但是大家仔細想一下問題在哪裏?

是的,不止你一個人監聽這個消息啊,還有別的服務也在監聽,他們也會失敗啊,他一失敗他也要求重發,但是你這裏其實是成功的,重發了,你的錢不就加了兩次了?

對不對???是不是這個道理???

還不理解?看下面

就好比上面的這樣,我們的積分系統處理失敗了,他這個系統肯定要求你重新發送一次這個消息對吧,積分的系統重新接收並且處理成功了,但是別人的活動,優惠券等等服務也監聽了這個消息呀,那不就可能出現活動系統給他加GMV加兩次,優惠券扣兩次這種情況麼?

真實的情況其實重試是很正常的,服務的網絡抖動開發人員代碼Bug,還有數據問題等都可能處理失敗要求重發的。

嗯小夥子分析得很仔細嘛,那你在開發過程中是怎麼去保證的呀?

一般我們叫這樣的處理叫接口冪等

冪等(idempotent、idempotence)是一個數學與計算機學概念,常見於抽象代數中。

在編程中一個冪等操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。

冪等函數,或冪等方法,是指可以使用相同參數重複執行,並能獲得相同結果的函數。這些函數不會影響系統狀態,也不用擔心重複執行會對系統造成改變。

例如,“setTrue()”函數就是一個冪等函數,無論多次執行,其結果都是一樣的.更復雜的操作冪等保證是利用唯一交易號(流水號)實現.

通俗了講就是你同樣的參數調用我這個接口,調用多少次結果都是一個,你加GMV同一個訂單號你加一次是多少錢,你加N次都還是多少錢。

但是如果不做冪等,你一個訂單調用多次錢不就加多次嘛,同理你退款調用多次錢也就減多次了。

大致處理流程如下:

那怎麼保證呢?

一般帥丙我是這麼回答的:

帥氣面試官您好,一般冪等,我會分場景去考慮,看是強校驗還是弱校驗,比如跟金錢相關的場景那就很關鍵呀,就做強校驗,別不是很重要的場景做弱校驗。

強校驗:

比如你監聽到用戶支付成功的消息,你監聽到了去加GMV是不是要調用加錢的接口,那加錢接口下面再調用一個加流水的接口,兩個放在一個事務,成功一起成功失敗一起失敗

每次消息過來都要拿着訂單號+業務場景這樣的唯一標識(比如天貓雙十一活動)去流水錶查,看看有沒有這條流水,有就直接return不要走下面的流程了,沒有就執行後面的邏輯。

之所以用流水錶,是因爲涉及到金錢這樣的活動,有啥問題後面也可以去流水錶對賬,還有就是幫助開發人員定位問題。

有的小夥伴可能還是有點懵,然後人才交流羣的小夥伴也說有些例子可以放一點僞代碼,那這期開始能用代碼將的我也寫點。

TipGItHub https://github.com/JavaFamily 上有進羣方式和個人聯繫方式,說實話在這個羣,哪怕您不說話,光看聊天記錄,都能學到東西(美團王炸,三歪(Java3y),並夕夕等的大佬都在)。

弱校驗:

這個簡單,一些不重要的場景,比如給誰發短信啥的,我就把這個id+場景唯一標識作爲Redis的key,放到緩存裏面失效時間看你場景,一定時間內的這個消息就去Redis判斷。

用KV就算消息丟了可能這樣的場景也沒關係,反正丟條無關痛癢的通知短信嘛(你敢說你沒驗證碼短信丟失的情況?)。

還有很多公司的弱校驗用token啊什麼的,反正花樣很多,但是重要的場景一定要強校驗,真正查問題的時候沒有在磁盤持久化的數據,心裏還是空空的,就像你和女朋友分開的時候的心裏狀態一樣。(我單身的怎麼知道這種感覺?猜的)

你們有接觸過消息順序消費這樣的場景麼?你怎麼保證的?

沒有!over!

乖,你肯定不能說沒有啊,就是算真的沒有,你看過敖帥丙的文章都要說有!

Tip:但是說實話順序消費這裏很難介紹,我上週到這周問了很多身邊的師兄開發過程中這樣的場景不多,我跟三歪也討論了幾次,網上更多的都是介紹binlog的同步,好像更多的場景就沒了。

一般都是同個業務場景下不同幾個操作的消息同時過去,本身順序是對的,但是你發出去的時候同時發出去了,消費的時候卻亂掉了,這樣就有問題了。

我之前做電商活動也是有這樣的例子,我們都知道數據量大的時候數據同步壓力還是很大的,有時候數據量大的表需要同步幾個億的數據。(並不是主從同步,主從延遲大的話會有問題,可能是從數據庫或者主數據庫同步到備庫

這種情況我們都是懟到隊列裏面去,然後慢慢消費的,那問題就來了呀,我們在數據庫同時對一個Id的數據進行了增、改、刪三個操作,但是你消息發過去消費的時候變成了改,刪、增,這樣數據就不對了。

本來一條數據應該刪掉了,結果在你那卻還在,這不是出大問題

兩者的結果是不是完全不一樣了

那你怎麼解決呢?

我簡單的說一下我們使用的RocketMQ裏面的一個簡單實現吧。

Tip:爲啥用RocketMQ舉例呢,這玩意是阿里開源的,我問了下身邊的朋友很多公司都有使用,所以讀者大概率是這個的話我就用這個舉例吧,具體的細節我後面會在RocketMQKafka各自章節說到。

生產者消費者一般需要保證順序消息的話,可能就是一個業務場景下的,比如訂單的創建、支付、發貨、收貨。

那這些東西是不是一個訂單號呢?一個訂單的肯定是一個訂單號的說,那簡單了呀。

一個topic下有多個隊列,爲了保證發送有序,RocketMQ提供了MessageQueueSelector隊列選擇機制,他有三種實現:

我們可使用Hash取模法,讓同一個訂單發送到同一個隊列中,再使用同步發送,只有同個訂單的創建消息發送成功,再發送支付消息。這樣,我們保證了發送有序。

RocketMQ的topic內的隊列機制,可以保證存儲滿足FIFO(First Input First Output 簡單說就是指先進先出),剩下的只需要消費者順序消費即可。

RocketMQ僅保證順序發送,順序消費由消費者業務保證!!!

這裏很好理解,一個訂單你發送的時候放到一個隊列裏面去,你同一個的訂單號Hash一下是不是還是一樣的結果,那肯定是一個消費者消費,那順序是不是就保證了?

真正的順序消費不同的中間件都有自己的不同實現我這裏就舉個例子,大家思路理解下。

Tip:我寫到這點的時候人才羣裏也有人問我,一個隊列有序出去,一個消費者消費不就好了,我想說的是消費者是多線程的,你消息是有序的給他的,你能保證他是有序的處理的?還是一個消費成功了再發下一個穩妥

你能跟我聊一下分佈式事務麼?

分佈式事務在現在遍地都是分佈式部署的系統中幾乎是必要的。

我們先聊一下啥是事務

分佈式事務事務隔離級別ACID我相信大家這些東西都耳熟能詳了,那什麼是事務呢?

概念:

一般是指要做的或所做的事情。

在計算機術語中是指訪問並可能更新數據庫中各種數據項的一個程序執行單元(unit)。

事務通常由高級數據庫操縱語言或編程語言(如SQL,C++或Java)書寫的用戶程序用戶程序的執行所引起,並用形如begin transactionend transaction語句(或函數調用)來界定。

事務由事務開始(begin transaction)和事務結束(end transaction)之間執行的全體操作組成。

特性:

事務是恢復和併發控制的基本單位。

事務應該具有4個屬性:原子性、一致性、隔離性、持久性。這四個屬性通常稱爲ACID特性

原子性(atomicity):一個事務是一個不可分割的工作單位,事務中包括的操作要麼都做,要麼都不做。

一致性(consistency):事務必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。

隔離性(isolation):一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的數據對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。

持久性(durability)持久性也稱永久性(permanence),指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。

那有同學還是不理解,敖丙我總結了一下就是:事務就是一系列操作,要麼同時成功,要麼同時失敗。然後會從事務的 ACID 特性(原子性、一致性、隔離性、持久性)展開敘述

事務就是爲了保證一系列操作可以正常執行,它必須同時滿足 ACID 特性。

那什麼是分佈式事務呢?

大家可以想一下,你下單流程可能涉及到10多個環節,你下單付錢都成功了,但是你優惠券扣減失敗了,積分新增失敗了,前者公司會被薅羊毛,後者用戶會不開心,但是這些都在不同的服務怎麼保證大家都成功呢

聰明,分佈式事務,你看你都會搶答了!

Tip:真實的應用場景可能比我介紹的場景複雜數倍,我只是爲了舉例方便一下大家理解所以用了很簡單的例子。

我接觸和瞭解到的分佈式事務大概分爲:

  • 2pc(兩段式提交)
  • 3pc(三段式提交)
  • TCC(Try、Confirm、Cancel)
  • 最大努力通知
  • XA
  • 本地消息表(ebay研發出的)
  • 半消息/最終一致性(RocketMQ)

這裏我就介紹下最簡單的2pc(兩段式),以及大家以後可能比較常用的半消息事務也就是最終一致性,目的是讓大家理解下分佈式事務裏面消息中間件的作用,別的事務都大同小異,都有很多優點。

當然也都有種種弊端

例如長時間鎖定數據庫資源,導致系統的響應不快併發上不去

網絡抖動出現腦裂情況,導致事物參與者,不能很好地執行協調者的指令,導致數據不一致

單點故障:例如事物協調者,在某一時刻宕機,雖然可以通過選舉機制產生新的Leader,但是這過程中,必然出現問題,而TCC,只有強悍的技術團隊,才能支持開發,成本太高

不多BB了,我們開始介紹這個兩個事物吧。

2pc(兩段式提交) :

2pc(兩段式提交)可以說是分佈式事務的最開始的樣子了,像極了媒婆,就是通過消息中間件協調多個系統,在兩個系統操作事務的時候都鎖定資源但是不提交事務,等兩者都準備好了,告訴消息中間件,然後再分別提交事務。

但是我不知道大家看到問題所在沒有?

是的你可能已經發現了,如果A系統事務提交成功了,但是B系統在提交的時候網絡波動或者各種原因提交失敗了,其實還是會失敗的。

最終一致性

整個流程中,我們能保證是:

  • 業務主動方本地事務提交失敗,業務被動方不會收到消息的投遞。

  • 只要業務主動方本地事務執行成功,那麼消息服務一定會投遞消息給下游的業務被動方,並最終保證業務被動方一定能成功消費該消息(消費成功或失敗,即最終一定會有一個最終態)。

不過呢技術就是這樣,各種極端的情況我們都需要考慮,也很難有完美的方案,所以纔會有這麼多的方案三段式TCC最大努力通知等等分佈式事務方案,大家只需要知道爲啥要做,做了有啥好處,有啥壞處,在實際開發的時候都注意下就好好了,系統都是根據業務場景設計出來的,離開業務的技術沒有意義,離開技術的業務沒有底氣

還是那句話:沒有最完美的系統,只有最適合的系統。

面試結束

小夥子看不出來啊,還是有點東西的嘛,這幾個點都回答的不錯,明天你能跟我聊一下RocketMQ麼?

敖丙這章花了這麼多時間,不確定他寫不寫的完,心疼他。好想給他點贊啊,消息回溯也在單獨介紹消息中間件的時候介紹吧,這章篇幅有點長了。

總結

這章其實我寫的時間比之前的秒殺還要久,因爲順序消息這個場景我不知道怎麼講出來大家容易懂一點,最後就參考了網上的,順序消息的實際應用場景沒別的那麼廣泛,跟3y也聊了好幾次,最後定了這個binlog的場景。

總之就是這期創作源泉有點枯竭,這章是真的難寫,包括分佈式事務在實際開發過程中也是很複雜的環節,需要用的時候光是做設計都要很久,反正我的流程圖長得一匹。

我每次都想着寫得通俗易懂一點,這篇即使是這樣我覺得還是不夠通俗易懂,但是消息的場景就是這樣,還有大家加我也不要一上來就問我很多扣細節的點,自己多點思考我覺得可能幫助比我告訴你答案好很多吧

絮叨

敖丙我呀,這周有牌面喲,上了CSDN的原力計劃榜單,而且獎金高達50塊!!!

錢不多但是很開心,跟老媽聊到她也覺得我出息了,剛好她生日,以前我們這一家人就是那種不過生日的,不過呀今年我工作了,而且有牌面的我拿了的獎金就很關鍵,偷偷叫表弟悄悄去給她買了蛋糕和禮物🎁,嘻嘻,開心。🎂

DISS

這是博客園的一個網友在我文章下面的評論,說實話不知道大家怎麼看的,我只想說:呵呵!傻*

我不知道這個多年的經驗到底是怎麼樣子的多年的經驗,我本來其實不準備說出來的,因爲我發現我羣裏很多都是還沒畢業的大學生或者應屆生,那就假設我讀者還有很多這樣的學生,他們都沒社會經驗我怕他們被這樣的人給誤導了。

我記得我在羣裏說過:

我可以80%肯定的告訴大家他這個觀點就是扯淡,還有那20%我是認同他的謙虛那個觀點,但是謙虛難道不應該是我們對待事物最基本的態度嘛?

但是面試裝傻這個觀點?還有什麼不會要比你強的人這個觀點?技術人我相信也有面試官也在看我的文章,你們在面試的時候,我想遇到厲害的人巴不得招入麾下,爲自己衝鋒陷陣吧。

而且正常面試的時候你是1-3年的經驗,面試你的基本上都是3年以上的,然後依次順推,當然也有很多很厲害的Leader(我前東家Leader95年的,字節跳動某產品線很強的Leader96的等等)等大家工作了你就會發現有些東西沒有時間積累是學不到的,你要做的只是一步一個腳印踏實走好就好了。

那些人不管年輕與否能坐在那面試你肯定有他的原因,那你有什麼才華,你盡情施展,他沒那個度量包容你的優秀,這樣的公司不去也罷,但是技術人這樣的真的很少,程序員是一羣很崇拜能力的人。

所以面試你有啥都秀出來,把你的才華盡情的展示出來,風就在那,你只管飛翔。

鳴謝

涉及到分佈式事務的環節我參考了前大神同事:魯班(花名)的技術分享,很感謝他的文章給的思路,還有問題的解析!

每次寫我都會在羣裏問大家,下次大家都在我的交流羣裏面也可以多給我點意見,謝謝了。

看到沒,就很民主。(敖丙你個渣男,呸,自己不會就不寫!)

TipGItHub https://github.com/JavaFamily 上有進羣方式和個人聯繫方式,說實話在這個羣,哪怕您不說話,光看聊天記錄,都能學到東西(美團王炸,三歪(Java3y),並夕夕等的大佬都在)。

日常求贊

好了各位,以上就是這篇文章的全部內容了,能看到這裏的人呀,都是人才

我每週都會更新幾篇《吊打面試官》系列和互聯網常用技術棧相關的文章,非常感謝人才們能看到這裏,如果這個文章寫得還不錯,覺得「敖丙」我有點東西的話 求點贊👍 求關注❤️ 求分享👥 對暖男我來說真的 非常有用!!!

創作不易,各位的支持和認可,就是我創作的最大動力,我們下篇文章見!

敖丙 | 文 【原創】【轉載請聯繫本人】 如果本篇博客有任何錯誤,請批評指教,不勝感激 !


《吊打面試官》系列每週持續更新,可以關注我的公衆號「 JavaFamily 」第一時間閱讀和催更(公衆號比博客早一到兩篇喲),本文GitHub上已經收錄https://github.com/JavaFamily,有一線大廠面試點思維導圖,歡迎Star和完善,裏面也有我個人聯繫方式有什麼問題也可以直接找我,也有人才交流羣,我們一起有點東西。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章