metaq最佳實踐

2021-08-25 06:06:40 字數 3224 閱讀 8305

本文件旨在描述rocketmq使用過程中的一些最佳實踐,建議使用者這樣做,但是非必須。

2.1 傳送訊息注意事項

乙個應用盡可能用乙個topic,訊息子型別用tags來標識,tags可以由應用自由設定。只有傳送訊息設定了tags,消費方在訂閱訊息時,才可以利用tags在broker做訊息過濾。message.settags(「taga」);

每個訊息在業務層面的唯一標識碼,要設定到keys欄位,方便將來定位訊息丟失問題。伺服器會為每個訊息建立索引(雜湊索引),應用可以通過topic,key來查詢這條訊息內容,以及訊息被誰消費。由於是雜湊索引,請務必保證key盡可能唯一,這樣可以避免潛在的雜湊衝突。

// 訂單id

string orderid = 「20034568923546」;

message.setkeys(orderid);

訊息傳送成功或者失敗,要列印訊息日誌,務必要列印sendresult和key欄位。

send訊息方法,只要不拋異常,就代表傳送成功。但是傳送成功會有多個狀態,在sendresult裡定義。

對於精衛傳送順序訊息的應用,由於順序訊息的侷限性,可能會涉及到主備自動切換問題,所以如果sendresult中的status欄位不等於send_ok,就應該嘗試重試。對於其他應用,則沒有必要這樣。

對於訊息不可丟失應用,務必要有訊息重發機制

例如如果訊息傳送失敗,儲存到資料庫,能有定時程式嘗試重發,或者人工觸發重發。

2.2訊息傳送失敗如何處理

producer的send方法本身支援內部重試,重試邏輯如下:

至多重試3次。

如果傳送失敗,則輪轉到下乙個broker。

這個方法的總耗時時間不超過sendmsgtimeout設定的值,預設10s。所以,如果本身向broker傳送訊息產生超時異常,就不會再做重試。

以上策略仍然不能保證訊息一定傳送成功,為保證訊息一定成功,建議應用這樣做如果呼叫send同步方法傳送失敗,則嘗試將訊息儲存到db,由後台執行緒定時重試,保證訊息一定到達broker。

上述db重試方式為什麼沒有整合到mq客戶端內部做,而是要求應用自己去完成,我們基於以下幾點考慮

mq的客戶端設計為無狀態模式,方便任意的水平擴充套件,且對機器資源的消耗僅僅是cpu、記憶體、網路。

如果mq客戶端內部整合乙個kv儲存模組,那麼資料只有同步落盤才能較可靠,而同步落盤本身效能開銷較大,所以通常會採用非同步落盤,又由於應用關閉過程不受mq運維人員控制,可能經常會發生kill -9這樣暴力方式關閉,造成資料沒有及時落盤而丟失。

producer所在機器的可靠性較低,一般為虛擬機器,不適合儲存重要資料。

綜上,建議重試過程交由應用來控制。

2.3選擇oneway形式傳送

乙個rpc呼叫,通常是這樣乙個過程

客戶端傳送請求到伺服器

伺服器處理該請求

伺服器向客戶端返回應答所以乙個rpc的耗時時間是上述三個步驟的總和,而某些場景要求耗時非常短,但是對可靠性要求並不高,例如日誌收集類應用,此類應用可以採用oneway形式呼叫,oneway形式只傳送請求不等待應答,而傳送請求在客戶端實現層面僅僅是乙個os系統呼叫的開銷,即將資料寫入客戶端的socket緩衝區,此過程耗時通常在微秒級。

3.1消費過程要做到冪等(即消費端去重)

如《rocketmq 原理簡介》中所述,rocketmq無法避免訊息重複,所以如果業務對消費重複非常敏感,務必要在業務層面去重,有以下幾種去重方式

將訊息的唯一鍵,可以是msgid,也可以是訊息內容中的唯一標識字段,例如訂單id等,消費之前判斷是否在db或tair(全域性kv儲存)中存在,如果不存在則插入,並消費,否則跳過。(實際過程要考慮原子性問題,判斷是否存在可以嘗試插入,如果報主鍵衝突,則插入失敗,直接跳過)

msgid一定是全域性唯一識別符號,但是可能會存在同樣的訊息有兩個不同msgid的情況(有多種原因),這種情況可能會使業務上重複消費,建議最好使用訊息內容中的唯一標識欄位去重。

使用業務層面的狀態機去重

3.3消費速度慢處理方式

3.3.1提高消費並行度

絕大部分訊息消費行為屬於io密集型,即可能是運算元據庫,或者呼叫rpc,這類消費行為的消費速度在於後端資料庫或者外系統的吞吐量,通過增加消費並行度,可以提高總的消費吞吐量,但是並行度增加到一定程度,反而會下降,如圖所示,呈現拋物線形式。所以應用必須要設定合理的並行度。cpu密集型應用除外。

3.3.2批量方式消費

某些業務流程如果支援批量方式消費,則可以很大程度上提高消費吞吐量,例如訂單扣款類應用,一次處理乙個訂單耗時1秒鐘,一次處理10個訂單可能也只耗時2秒鐘,這樣即可大幅度提高消費的吞吐量,通過設定consumer的consumemessagebatchmaxsize這個引數,預設是1,即一次只消費一條訊息,例如設定為n,那麼每次消費的訊息數小於等於n。

3.3.3跳過非重要訊息

發生訊息堆積時,如果消費速度一直追不上傳送速度,可以選擇丟棄不重要的訊息

如何判斷消費發生了堆積?

public consumeconcurrentlystatus consumemessage

(list

msgs, consumeconcurrentlycontext context)

// todo正常消費過程

return consumeconcurrentlystatus.consume_success;

}

如以上**所示,當某個佇列的訊息數堆積到100000條以上,則嘗試丟棄部分或全部訊息,這樣就可以快速追上傳送訊息的速度。

3.3.4優化每條訊息消費過程

舉例如下,某條訊息的消費過程如下

1.根據訊息從db查詢資料1

2.根據訊息從db查詢資料2

3.複雜的業務計算

4.向db插入資料3

5.向db插入資料4

這條訊息的消費過程與db互動了4次,如果按照每次5ms計算,那麼總共耗時20ms,假設業務計算耗時5ms,那麼總過耗時25ms,如果能把4次db互動優化為2次,那麼總耗時就可以優化到15ms,也就是說總體效能提高了40%。

3.4 消費列印日誌

如果訊息量較少,建議在消費入口方法列印訊息,方便後續排查問題。

public consumeconcurrentlystatus consumemessage

(list

msgs, consumeconcurrentlycontext context)

}

如果能列印每條訊息消費耗時,那麼在排查消費慢等線上問題時,會更方便。

metaq最佳實踐

本文件旨在描述rocketmq使用過程中的一些最佳實踐,建議使用者這樣做,但是非必須。2.1 傳送訊息注意事項 乙個應用盡可能用乙個topic,訊息子型別用tags來標識,tags可以由應用自由設定。只有傳送訊息設定了tags,消費方在訂閱訊息時,才可以利用tags在broker做訊息過濾。mess...

最佳實踐 Flutter 最佳實踐

最佳實踐是乙個領域可以接受的專業標準,對於任何程式語言來說,提高 質量 可讀性 可維護性和健壯性都非常重要。讓我們探索一些設計和開發flutter應用程式的最佳實踐。class enum typedef和extension應採用駝峰命名uppercamelcase規則。class mainscree...

JUnit最佳實踐

junit最佳實踐 cherami 轉貼 參與分 20053,專家分 4960 發表 2003 9 16 下午7 57 版本 1.0 閱讀 3899次 martin fowler說過 當你試圖列印輸出一些資訊或除錯乙個表示式時,寫一些測試 來替代那些傳統的方法。一開始,你會發現你總是要建立一些新的f...