本文件旨在描述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...