訊息佇列之訊息過濾

2021-10-08 06:32:54 字數 1807 閱讀 9504

眾所周知,rocketmq是支援訊息過濾的,即傳送訊息時,可以給訊息設定乙個tag。訂閱主題的時候,可以設定只消費攜帶某些tag的訊息,起到訊息過濾的作用。

rocketmq中是把訊息tag通過雜湊轉換成了的long型,儲存在了訊息索引中。在訂閱客戶端拉取訊息時,為了減少協議大小,減低報文長度,拉取協議中也只攜帶指定的若干tag的雜湊值,服務端接收到拉取請求時,在查詢索引時,就直接過濾了不符合條件的索引。這裡就會有乙個問題,如果出現雜湊衝突了,就會有錯誤的訊息被客戶端消費到。rocketmq為了提高效能,並沒有在服務端對訊息進行反序列化,所以精準的二次過濾是在客戶端完成。當客戶端收到拉取結果後,會通過反序列化出來的訊息內容,用tag原始值再次過濾,保證過濾的準確性。

以上是rocketmq訊息過濾的基本做法,因為索引必須是固定大小的,這樣才能通過偏移量,直接計算出索引的位置。所以在儲存tag時,要把tag轉換為固定長度,即通過雜湊的方式轉換成了long。這裡會產生乙個限制,即一條訊息,只能攜帶乙個tag標籤。

如果一條訊息要攜帶多個tag標籤,又該如何做。

最簡單想到的,就是tag不做轉換,也不存放在訊息索引中了,拉取訊息時,服務端把訊息反序列化,直接通過訊息內容中的tag原始值進行過濾。該方法肯定不可取,對效能影響很大。所以我們還需要一種既可以支援單條訊息多個tag,也不影響效能的方案。

接下來是我的乙個思考過程。

不影響效能,就必須利用到索引,在查詢索引時就過濾掉絕大部分不符合條件的訊息。那麼問題就轉換成了如何把多個tag儲存到固定長度的訊息索引中?

即我們怎麼把多個tag對映到乙個固定的長度上,並且對映後的結果,可以準確地、或幾乎準確地判斷出哪些tag是符合條件的。

搜尋大腦,首先想到的就是布隆過濾器,原始的布隆過濾器肯定無法滿足,我們只採用其思想。我們把索引中的long看做乙個64位的bitmap,每一位初始化為0。每個tag通過n個雜湊演算法,對映的64位中的n位上。在拉取訊息時,只需要採用相同的n個雜湊演算法,計算訂閱設定的tag對映的n個位置,如果這n個位置都為1,則說明該索引對應的訊息可能包含該tag,二次精準的過濾依舊在客戶端進行;否則,一定不符合條件,直接過濾掉。

該方式可以滿足我們的要求,但是每個tag都要計算n次雜湊,會對訊息儲存和拉取都產生一定的效能影響。並且擴充套件性較差,如果把雜湊演算法放在客戶端,則較難擴充套件。如果把雜湊演算法放在服務端,則拉取協議需要攜帶原始tag,又會增大報文長度。

我們從另乙個角度來看,我們所有tag生成的乙個固定長度的值,如果可以準確過濾出不符合條件的tag,是不是意味著我們生成的值一定包含了所有tag的基因。

接著我們再看一組公式,假設有兩個二進位制數x和y,如果x&y=x,則x中所有值為1的位,在y中一定也為1;反之不一定成立。也就可以說y包含了x的所有基因。如何讓乙個二進位制數包含另乙個二進位制數的所有基因呢?

很容易想到,利用按位或的方式,即只需要y=s|x,其中s為任意二進位制數。

所以,x&y===x,當y=s|x時,其中s為任意二進位制數。

擴充套件一下,有二進位制數集合p,一定有(p1|p2|p3|......)&pn===pn。

回到我們的tag中,我們依舊把tag通過雜湊轉換為long,索引中儲存所有tag的雜湊值按位或的結果。當拉取訊息時,通過訂閱設定的tag雜湊值與索引中的雜湊值進行按位與操作,如果結果等於訂閱設定的tag雜湊值,說明該索引對應的訊息可能符合條件,二次精準過濾依舊在客戶端來做;否則,一定不符合條件,直接過濾掉。

至此,簡單說明了兩種如何讓rocketmq單條訊息支援多tag的方案。實際都較為簡單,實現起來也不複雜,兩種我都已經做了實現,但是聯想過程還是挺有意思,看似不相關的知識點,可能就會帶來意想不到的靈感。

訊息佇列 訊息佇列

輪詢排程 一次性分發所有訊息,保證訊息平均分配,不管消費者是否能正常消費 訊息應答 保證消費端能確實消費,不丟失 公平 乙個乙個分發所有訊息,在保證分發到的執行緒確認回覆後,才分發下個訊息給下個空閒的消費者,訊息持久化 保證佇列中的訊息不丟失,包括3要素 交換器 訊息佇列 訊息都必須宣告持久化 發布...

訊息佇列 訊息佇列 kafka

kafka是乙個分布式的基於發布 訂閱模式的訊息佇列,主要用於大資料實時處理領域。要理解kafka首先要有分布式的概念,要有訊息佇列的概念。分布式系統最大的優勢就是解耦和削峰,這種情況下,a系統生成了乙個訊息,b系統非同步獲取,那麼就需要乙個存放訊息的訊息佇列 mq 相比較傳統的訊息佇列,訊息被消費...

訊息和訊息佇列

在傳統的c 程式當中,我們呼叫 fopen 函式開啟檔案,這個庫函式最終呼叫作業系統 提供的函式 來開啟檔案。而在 windows 中,不僅使用者程式可以呼叫系統的 api函式,反回來,系統也會呼叫使用者程式,這個呼叫是通過訊息來進行的。windows程式設計是一種完全不同於傳統的 dos方式的程式...