摘要:生產(chǎn)者生產(chǎn)的消息要滿足不了消費(fèi)者才行。可以看到一個(gè)有依賴的消息我們在處理的過程,會多一次查詢操作,性能多少會受點(diǎn)影響。如果沒有的消息進(jìn)來,孤兒院里是醬紫的。收到之后再處理,緊接著又找到的條消息,再出來,讓去處理。
在項(xiàng)目中踏完一系列坑后總結(jié)出來,消息的處理有兩個(gè)要?jiǎng)?wù):
消費(fèi)一定要快,我們喜歡供小于求的市場。生產(chǎn)者生產(chǎn)的消息要滿足不了消費(fèi)者才行。
任何消息都不能丟,因?yàn)檫@都是數(shù)據(jù)啊,即使處理不了也得找地方存著。最好每次的消息都存著,之后就變成了event sourcing(另一個(gè)大坑)。
要實(shí)現(xiàn)上述2點(diǎn),其實(shí)要解決很多問題。一個(gè)快字就不是那么做到的。業(yè)務(wù)系統(tǒng)收到消息有可能會觸發(fā)一連串的,并且包裹著事務(wù)的邏輯。因?yàn)橥ǔN覀兿M绻@一連串的處理失敗的話,可以把a(bǔ)ck退回給MQ。一旦業(yè)務(wù)邏輯過于復(fù)雜,work消費(fèi)消息的速度也會變慢。這就需要開發(fā)人員去做權(quán)衡了,是不是有些非常heavy的操作可以先記一筆,等業(yè)務(wù)不繁忙的時(shí)候再做。具體實(shí)現(xiàn)不在這篇討論。
問題回到主題,有一類消息最讓人頭疼,就是消息之間有依賴,關(guān)系一般為單向的父子關(guān)系。舉個(gè)栗子,Product和SKU的關(guān)系,一個(gè)Product包含多個(gè)SKU。比如我們的業(yè)務(wù)邏輯是要監(jiān)聽這兩個(gè)消息組成一顆樹放到索引中。可想而知,這棵樹肯定是至少兩層結(jié)構(gòu),第一級是Product,下面掛著一個(gè)或多個(gè)SKU。
一般來說,子結(jié)構(gòu)如果是個(gè)多帶帶的消息肯定會有個(gè)字段說明自己的parent id是什么。那么很自然的,我們在某一刻只收到一個(gè)SKU的Create事件,會去通過parent id找到索引中對應(yīng)的Product,然后掛上去。問題來了,要是索引中沒有對應(yīng)的Product怎么辦,消息是沒有順序的,可能是Product的Create事件還沒處理到,或者是生產(chǎn)者出了bug消息沒發(fā)出來造成的。這時(shí)SKU的消息就成了孤兒消息。
解決思路一比較近粗暴的方式,就是利用SKU上的parent id虛擬出一個(gè)只有id的Product,由處理SKU事件的worker來幫忙創(chuàng)建這個(gè)Product。等下次Product的Create消息進(jìn)來做一次更新就好了。(處理消息應(yīng)該不要區(qū)分這是Create還是Update還是Delete,消費(fèi)者就都當(dāng)Update來做比較好,可以想想為什么)。
當(dāng)然這個(gè)思路一看就有點(diǎn)bad smell,從單一職責(zé)的角度上來看,處理SKU的worker應(yīng)該只關(guān)注SKU,不應(yīng)該關(guān)注Product。如果Product也是個(gè)孤兒怎么辦呢?這個(gè)worker可能會越寫越復(fù)雜。
改進(jìn)的話可以把創(chuàng)建虛擬Product的這個(gè)事情放到SKU這個(gè)對象中去做,實(shí)現(xiàn)以下setProduct這個(gè)方法。那么即使Product也有依賴,那Product自己也得有個(gè)setParent的方法,這樣就可以遞歸下去了。(之后想了一下無法處理多級關(guān)系,因?yàn)镻roduct的消息沒來,我們不知道它的parent id,父節(jié)點(diǎn)根本建不出來。所以思路一只能處理一個(gè)層級的依賴。)
總結(jié)一下,思路一是一種不管三七二十一,誰也不能阻止我消費(fèi)的路線,大不了自下而上的創(chuàng)建虛擬父節(jié)點(diǎn)。
解決思路二相對思路一而言,這種思路還是比較優(yōu)雅的,但是優(yōu)雅不等于性能好。
既然SKU是個(gè)孤兒,那么我們先收下來放孤兒院好了。新建一張孤兒院表:
id | parent_type | parent_id |
---|---|---|
101010 | Product | 1010 |
101011 | Product | 1010 |
上面兩條數(shù)據(jù)就是SKU的,然后為了提升一點(diǎn)性能我們得對對象分個(gè)類,一類是有依賴的,一類是無依賴的。沒有依賴的直接消費(fèi)就好,像Product,SKU這種有依賴的,都得打上標(biāo)簽(就是對象里寫個(gè)isDependency)。例如一個(gè)SKU(101010)的消息進(jìn)來,worker發(fā)現(xiàn)這是一個(gè)有依賴的消息,那么先拿parent id (1010)去找Product, 發(fā)現(xiàn)Product找不到就把這個(gè)SKU丟到孤兒院表里去。如果你是用OO的語言,這里其實(shí)可以抽象一下。一個(gè)BaseWorker,一個(gè)SKUWorker,BaseWork負(fù)責(zé)寫個(gè)abstract的findParent(),SKUWorker去實(shí)現(xiàn)找Product的邏輯就好了。
public abstract class BaseWorker{ public void handle(T t) { if (t.isDependency() && findParent(t) == null) { // 送到孤兒院 takeToOrphanage(t); return; } } abstract Entity findParent(T t); protected void takeToOrphanage(T t) { } }
消息記錄下來以后,Worker的工作就終止,等待下一條消息進(jìn)來。過了幾分鐘,Product(1010)的消息過來了,這時(shí)候我們需要給BaseWorker再添加一些代碼。
public void handle(T t) { if (t.isDependency() && findParent(t) == null) { // 送到孤兒院 takeToOrphanage(t); return; } // 正常業(yè)務(wù)... // 正常業(yè)務(wù)處理之后 if (t.isDependency()) { Listchildren = findChildren(t); if (children != null) { children.forEach(child -> { sendAsMessage(child); }); } } } abstract List findChildren(T t);
我們增加一個(gè)findChildren方法,讓ProductWorker去實(shí)現(xiàn)具體邏輯。handle()中增加的代碼含義是,當(dāng)Product這個(gè)消息消費(fèi)完了以后,去孤兒院轉(zhuǎn)一圈看看是不是有等待認(rèn)領(lǐng)的孩子,簡單的利用parent_type和parent_id就能查到。查到以后別直接處理,仍然是以消息的形式發(fā)出,讓SKUWorker自己去handle,然后可以delete/soft-delete孤兒院中的記錄。
可以看到一個(gè)有依賴的消息我們在處理的過程,會多一次查詢操作,性能多少會受點(diǎn)影響。之前的那次findParent查詢其實(shí)思路一也有的,目的就是掛靠。
再多一個(gè)層級看看是不是罩得住,Category --> Product --> SKU 三層。
如果沒有Category的消息進(jìn)來,孤兒院里是醬紫的。
id | parent_type | parent_id |
---|---|---|
101010 | Product | 1010 |
101011 | Product | 1010 |
1010 | Category | 10 |
某一時(shí)刻Category的消息進(jìn)來,CategoryWorker會先到表里查到一條1010的Product消息,把它send出來。ProductWorker收到之后再處理,緊接著又找到SKU的2條消息,再send出來,讓SKUWorker去處理。可以看到,自帶遞歸,多層級只要是單向依賴的肯定搞的定。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/66305.html
摘要:本地安裝時(shí),遇到如下的錯(cuò)誤消息解決方案點(diǎn)擊的菜單點(diǎn)擊標(biāo)簽頁,在里維護(hù)記錄將錯(cuò)誤信息里提到的維護(hù)進(jìn)點(diǎn)擊按鈕重新啟動之后錯(cuò)誤消息消失。 本地安裝Kubernetes時(shí),遇到如下的錯(cuò)誤消息: pleade add --insecure-registry gcr.io to daemons arguments showImg(https://segmentfault.com/img/remot...
摘要:因?yàn)橄M(fèi)消息是在另外一個(gè)進(jìn)程中,我們需要阻塞我們的進(jìn)程直到結(jié)果返回,使用阻塞隊(duì)列是一種非常好的方式,這里我們使用了長度為的,的功能是檢查消息的的是不是我們之前所發(fā)送的,如果是,將返回值返回到。 推廣 RabbitMQ專題講座 https://segmentfault.com/l/15... CoolMQ開源項(xiàng)目 我們利用消息隊(duì)列實(shí)現(xiàn)了分布式事務(wù)的最終一致性解決方案,請大家圍觀。可以參考...
摘要:在本地安裝時(shí),遇到錯(cuò)誤消息這個(gè)原因是應(yīng)用沒有正確設(shè)置代理。在上設(shè)置代理非常方便選擇即手動設(shè)置。設(shè)置完之后,點(diǎn)擊按鈕之后在里使用命令行可以成功把鏡像下載到本地。 在本地安裝Kubernetes時(shí),遇到錯(cuò)誤消息: request canceled while waiting for connection(Client.Timeout exceeded while awaiting head...
摘要:為程序員金三銀四精心挑選的余道面試題與答案,歡迎大家向我推薦你在面試過程中遇到的問題我會把大家推薦的問題添加到下面的常用面試題清單中供大家參考。 為Java程序員金三銀四精心挑選的300余道Java面試題與答案,歡迎大家向我推薦你在面試過程中遇到的問題,我會把大家推薦的問題添加到下面的常用面試題清單中供大家參考。 前兩天寫的以下博客,大家比較認(rèn)可,熱度不錯(cuò),希望可以幫到準(zhǔn)備或者正在參加...
閱讀 3049·2021-11-18 10:02
閱讀 3315·2021-11-02 14:48
閱讀 3384·2019-08-30 13:52
閱讀 527·2019-08-29 17:10
閱讀 2070·2019-08-29 12:53
閱讀 1392·2019-08-29 12:53
閱讀 1018·2019-08-29 12:25
閱讀 2155·2019-08-29 12:17