位元組跳動面試官這樣問訊息佇列:分散式事務、重複消費、順序消費,我整理了一下

敖 丙發表於2020-02-16

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

點贊再看,養成習慣

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不要走下面的流程了,沒有就執行後面的邏輯。

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

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

弱校驗:

這個簡單,一些不重要的場景,比如給誰發簡訊啥的,我就把這個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的等等)等大家工作了你就會發現有些東西沒有時間積累是學不到的,你要做的只是一步一個腳印踏實走好就好了。

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

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

鳴謝

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

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

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

白嫖不好,創作不易,各位的點贊就是丙丙創作的最大動力,我們下篇文章見,文末圖片有福利

持續更新,未完待續……


文章每週持續更新,可以微信搜尋「 三太子敖丙 」第一時間閱讀,回覆【資料】【面試】有我準備的一線大廠面試資料和文章,本文 GitHub https://github.com/JavaFamily 已經收錄,有大廠面試完整考點,歡迎Star。

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

相關文章