作者:小傅哥,博客:https://bugstack.cn
大家好,我是技術(shù)UP主小傅哥。
如果你的MQ消息要從Kafka切換到RocketMQ且不停機(jī),怎么做?在讓這個(gè)MQ消息調(diào)用第三方發(fā)獎(jiǎng)接口,但無(wú)冪等字段又怎么處理?今天小傅哥就給大家分享一個(gè)關(guān)于MQ消息在這樣的場(chǎng)景中的處理手段。
這是一種比較特例的場(chǎng)景,需要保證切換的MQ消息不被兩端同時(shí)消費(fèi),并且還需要在一段消費(fèi)失敗后的MQ還可以繼續(xù)重試。并且這一端消費(fèi)的MQ消息,也要保證自身的冪等。
我們知道一般通用場(chǎng)景下,MQ消息都會(huì)有一個(gè)業(yè)務(wù)唯一ID值,用于接收方做仿重處理。但除此之外還應(yīng)該有一個(gè)MQ消息本身的ID,這個(gè)ID也要全局唯一,每一條消息都要有一個(gè)ID,這是因?yàn)镸Q是可能重復(fù)發(fā)送的(發(fā)送MQ成功,但獲取MQ發(fā)送結(jié)果響應(yīng)超時(shí)或更新庫(kù)表消息狀態(tài)失敗,則重復(fù)發(fā)送),如果沒有消息的唯一ID也就沒法確保是哪一條消息了。
這個(gè)ID可以用于;唯一標(biāo)識(shí)、去重、鏈路追蹤、冪等性、事務(wù)以及安裝性等,但可能有些伙伴在做MQ消息發(fā)送的時(shí)候,是容易忽略而沒有在MQ中添加這個(gè)ID,或者隨意用時(shí)間戳來(lái)當(dāng)ID用,這樣都是不合理的。會(huì)影響一些場(chǎng)景的代碼健壯性設(shè)計(jì)。
需求背景描述好了,接下來(lái),我們看看這樣的場(chǎng)景怎么設(shè)計(jì)。
1. 場(chǎng)景問(wèn)題
將原本使用 Kafka 的MQ方式,遷移到 RocketMQ,同時(shí)部分場(chǎng)景的 MQ 消息調(diào)用三方接口是沒有冪等字段的,需要做好程序兼容處理。
2. 場(chǎng)景思考
首先我們要知道在分布式架構(gòu)下,我們每做的技術(shù)方案都要考慮順序性和臨界狀態(tài)。像是MQ的生產(chǎn)和消費(fèi)都是多套應(yīng)用實(shí)例部署的,那么生產(chǎn)端發(fā)送出來(lái)的MQ消息到不同的隊(duì)列中也是有延遲和存放順序以及拉取消費(fèi)不同的情況。如;生產(chǎn)端發(fā)送MQ為A、B、C、D,但到Kafka/RocketMQ以及不同的消費(fèi)端拉取時(shí),不一定是A、B、C、D的順序,那么直接做切量開關(guān),是可能導(dǎo)致一個(gè)A消息在Kafka隊(duì)列中消費(fèi)完,點(diǎn)擊切換開關(guān)(一種切量哈希計(jì)算手段,如消息{A}哈希值最后兩位當(dāng)做百分比用),正好RocketMQ也會(huì)把A消費(fèi)掉。這樣同一個(gè)消息就被重復(fù)消費(fèi)了。
3. 方案設(shè)計(jì)
在整個(gè)方案設(shè)計(jì)中,我們要考慮幾個(gè)非常重要的點(diǎn)。如圖;
-
- 一個(gè)是切換的兩端MQ消費(fèi)是搶占式加鎖,避免重復(fù)消費(fèi)。這是因?yàn)榍辛块_關(guān),切換過(guò)程中,兩個(gè)消息隊(duì)列中的MQ并不是順序可靠的,可能存在重復(fù)消費(fèi),所以要加分布式鎖。一段MQ消費(fèi)失敗要進(jìn)行重試,但這個(gè)時(shí)候不能在消費(fèi)失敗后刪分布式鎖,因?yàn)镸Q消費(fèi)都是很快的,可能導(dǎo)致刪鎖后另外一端MQ進(jìn)行了相同的消費(fèi)。那可能有些伙伴會(huì)說(shuō),那也沒關(guān)系呀,反正失敗的這段沒有消費(fèi)成功。當(dāng)往往失敗并不一定是直接的結(jié)果失敗,可能是網(wǎng)絡(luò)失敗,可能是超時(shí)失敗等。也就是實(shí)際成功了,但超時(shí)反饋了。所以不能被其他端重復(fù)消費(fèi),并且要保證自己這一端消費(fèi)失敗后可重試。所以這塊要設(shè)計(jì)可重入鎖,也就是 setnx 加鎖的值,為自身一段的 mq 類型,這樣自己在接收mq消息以后,檢查鎖為自身加鎖值可重試。這樣也就保證了一端消費(fèi)重試,不會(huì)讓另外一端把MQ也跟著消費(fèi)掉,因?yàn)閟etnx存在,并且有加鎖值判斷,所以不能進(jìn)入。另外MQ消息還可能存在同一個(gè)MQ發(fā)送多次的場(chǎng)景,這個(gè)是非常正常的。比如,你再發(fā)送MQ的時(shí)候,超時(shí)網(wǎng)絡(luò)抖動(dòng)失敗(1萬(wàn)次會(huì)有1次),那么就會(huì)補(bǔ)償重發(fā)。但這個(gè)MQ已經(jīng)發(fā)送過(guò)了,所以會(huì)接收2條MQ消息。那么在消費(fèi)的時(shí)候,不能讓2個(gè)MQ消息都進(jìn)入消費(fèi)中,因?yàn)槎嗯_(tái)實(shí)例消費(fèi),可能都去調(diào)用發(fā)獎(jiǎng)了。那么這里還需要給MQ的ID進(jìn)行冪等加鎖。確保一個(gè)MQ消息,失敗后,順序輪訓(xùn)重試。也就保證了,發(fā)獎(jiǎng)的過(guò)程中不會(huì)出現(xiàn)超發(fā)獎(jiǎng)品。
大部分三方接口還是有冪等字段的,有的話會(huì)更好。
-
-
- 另外還有2個(gè)開關(guān),一個(gè)是
消費(fèi)開關(guān)
-
- ,一個(gè)是
切量開關(guān)
- 。消費(fèi)開關(guān)要在整個(gè)新的MQ改造工程工程全部上線后開啟,但還要被切量開關(guān)限定消費(fèi)。開啟后,切量開關(guān)才會(huì)生效。切量是一種哈希值的百分比比對(duì),比如一個(gè)哈希值最后兩位是10,那么切量配置小于等于10%則這個(gè)MQ則可以被切量后消費(fèi),另外一段則不消費(fèi)這個(gè)MQ。另外,為了方便測(cè)試線上功能,還會(huì)加入白名單。不過(guò)大部分時(shí)候這類東西會(huì)用通用組件能力解決。
-
這樣的場(chǎng)景方案設(shè)計(jì),是非常值得積累的,同類的思想也可以幫我們解決很多共性問(wèn)題。
4. 加入學(xué)習(xí)
注意,像這樣的場(chǎng)景方案,在【星球:碼農(nóng)會(huì)鎖】實(shí)戰(zhàn)項(xiàng)目中有非常多的運(yùn)用。還包括:大營(yíng)銷、OpenAI 應(yīng)用、API網(wǎng)關(guān)、Lottery抽獎(jiǎng)、IM通信、SpringBoot Starter 組件開發(fā)、IDEA Plugin 插件開發(fā)、支付SDK、動(dòng)態(tài)線程組件、透視業(yè)務(wù)監(jiān)控等,并還有開源項(xiàng)目學(xué)習(xí)。
如果大家希望通過(guò)做有價(jià)值的編程項(xiàng)目,提高自己的編程思維和編碼能力,可以加入小傅哥的【星球:碼農(nóng)會(huì)鎖】。加入后解鎖所有往期項(xiàng)目,還可以學(xué)習(xí)后續(xù)新開發(fā)的項(xiàng)目。
這樣成體系的全量項(xiàng)目學(xué)習(xí),放在一些平臺(tái)售賣,至少都要上千塊。但小傅哥的星球,只需要100多,就可以獲得大廠架構(gòu)師對(duì)你手把手教學(xué)!
星球全程手把手指導(dǎo)教學(xué),遇到技術(shù)問(wèn)題幫忙排查代碼。已經(jīng)有很多伙伴開始學(xué)起來(lái)了,還有大家交的作業(yè)筆記。有了的項(xiàng)目驅(qū)動(dòng)學(xué)習(xí),清晰的目標(biāo)感,大家沖起來(lái)也有了更明確的方向!干干干?。?!
在今年的面試中,星球幫助眾多伙伴拿到微信支付
、京東科技
、度小滿
、螞蟻金服
、Lazada(電商優(yōu)惠營(yíng)銷)
、快手
、美團(tuán)到店
等Offer,還有的校招生薪資最高年包到45w!