技術幹貨丨如何在VIPKID中構建MQ服務
小結:
1、
https://mp.weixin.qq.com/s/FQ-DKvQZSP061kqG_qeRjA
文 |李偉 VIPKID數據中間件架構師
交流微信 | datapipeline2018
本文完整PPT獲取 | 關註公眾號後,後臺回復“李偉”
本文整理自丨4月13日,DataPipeline聯合Apache RocketMQ在北京舉辦的消息中間件Meetup
本文主要從三方面展開。首先介紹VIPKID使用MQ的歷史,即如何演化到現在的RocketMQ + Kafka。其次,為RocketMQ在VIPKID的實踐,主要介紹如何解決在使用RocketMQ過程中出現的相關問題。最後,提到VIPKID的MQ服務關於以後可能會如何變化和發展。
MQ History in VIPKID
關於VIPKID的需求:
第一性能。VIPKID是貫穿北美和中國的線上英語學習機構,數據、消息量龐大,對於MQ吞吐量、延遲等有極度的要求。
第二海量的topic。因為業務方的業務場景相對比較復雜,不同於常規,所以對多topic的要求較高。目前在測試環節,包含的topic應該有3000+,消費者有超過10k+。
第三統一的管理平臺。由於各種topic,消費者紛繁復雜, 需要一個像Operator那樣可以在平臺上“指指畫畫”即可管理topic,消費者,和相關負責人的信息的平臺。
第四友好的API。對RD同學而言,不需花費過多的精力學習,只需看API便能上手接入。
另外對提供MQ服務的同學而言,方便二次開發。中間件大部分選擇用Java,如果使用其它語言,比如GO,Scala,對二次開發的難度會比較大,維護成本也會很高。
考慮到學習成本,以上為我們在之前面臨的一些問題和訴求,基於這些訴求,我們在之前就開始做選擇。
公司之前使用MQ服務時,管理簡單。這也讓我們面臨一些較為尷尬的問題。
之後經過層層篩選,各種把關,選擇了RocketMQ+Kafka,RocketMQ主要提供給業務方使用,做一些業務數據的流轉。包括像削峰填谷,或是一些異步數據等同步,另外還需同步一些BI數據給業務方。
為什麽會這麽選?現在市面上有許多比較成熟的MQ產品,比如像目前使用的Kafka,第二個是RocketMQ,第三個為RabbitMQ,最後決定Kafka先保持現狀,把業務型的ActiveMQ替換成RocketMQ。
同時,也積極和RocketMQ社區保持聯系,一來可以依靠社區力量將公司MQ服務做得更加穩定。 另外也可以向社區其他同學分享我們的實踐經驗。
為什麽Kafka會保持,原因在於對於ELK生態而言,Kafka目前是不可或缺的,在性能上可以滿足要求。
為什麽選擇RocketMQ,有以下原因:低延遲、百萬級topic、二次開發容易、管理部署方便。
下圖為系統的簡單結構圖,大概描述了生產消費的大概過程。還有Broker端的部署結構,最上面一層為生產過程,中間生產到了Broker的集群,下面是一個消費者的過程,最右邊為retry的隊列,主要做容錯。
比如發一個消息時不是一錘子買賣,當消息發過去之後,由於網絡抖了一下,接下來第二第三API失敗了,此時可不可以允許我再發一次消息,像這種容錯的情況是支持的,所以RocketMQ也是很重要的原因。
RocketMQ 最佳實踐
首先最重要的問題,也是研發同學反饋過來的問題中最難理解的一部分,即訂閱關系一致。
所謂的訂閱關系一致,指一個消費者組在一個服務中,假設消費者組消費了3個topic,該服務部署到任何一臺機器上,消費者組消費到topic都必須一樣,這就是訂閱關系一致。因為測試環節是多環境的,在多環境情況下會遇到訂閱關系不一致的情況,這屬於使用問題。
第二個是Docker,因為多環境情況下,在測試環節走的是K8S,它會啟動Docker容器,在Docker裏會面臨一個問題,消費者客戶端在起寫時,會將自己的訂閱信息上報給Broker,告訴它訂閱了哪些topic,以及消費者客戶端ID是多少,以此來表明身份。當在取客戶端ID時,取了一個本地IP,但是在Docker裏面取出來的本地大部分是相同的。
上述情況會出現類似於訂閱關系不一致的結果。會發現topic裏面有的消息、隊列沒有消費者去訂閱,實際是有的。因為Broker在做rebalance時,由於沒有辦法正常區分到底起了多少個消費者時令,導致分配隊列時出現問題。原因在於當上報訂閱關系給Broker時,數據有錯誤,這是Broker從本地IP取出來導致的。
第三個是多環境問題,假設有ABC三個服務,A服務依賴於B,B依賴於C。通常假設在開發一個服務時,會單獨拉一個分支進行開發,此時可能會與上一步的下一個服務做聯調。
同時在測試環節,還會有一個穩定的服務提供給其他人做聯調,在沒有開發時需要給QA做集成測試。這個環境中稱為多環境,在多環境的情況就會遇到訂閱關系不一致的問題。同時,在多環境下如何做消息路由也是需要考慮的問題。
首先解釋訂閱關系不一致,假設有兩個人開發兩個功能,其中一個是Host為1.1.1.1,消費者組有3個topic,1、2、3,當拿到一個新的需求後需要將topic3換成topic4,獲取消息。
在消費寫完之後,需要發到一個環境進行測試,假設將這兩個同時放到環境中,就會出現訂閱關系不一致,這時有可能會出現以下情況:
當添加topic4去掉topic3之後,在測試topic4是否能收到消息時,結果發現收不到。或是當發送一條消息之後,連續不斷地收到了100多萬條消息。
如果打開console平臺,發現有的隊列沒有消費者去消費,這就是所謂的訂閱關系不一致。
比如,同樣的一個ConsumerGroup1,第一臺機器消費了3個topic1、2、3,第二臺機器消費了topic1、2、4,這就是訂閱關系不一致。即同一個消費組部署的兩臺實力,消費者的topic不一樣,稱為訂閱關系不一致。
為此,在多環境的情況下制定了一個簡單規則。通常發這種分支時,是在多環境的子環境,比如,當發一個新功能時,需要將開發分支放到A1環境進行測試,這時就有一個簡單的規則,即不能跨環境生產消費。
假設一個開發分支是A1環鏡的,當消費消息時,可用A1環境的consumerGroup去消費A1環境的topic。如果要測試一下收的消息處理邏輯是否正確,就可以去控制臺模擬發送給A1的topic。
這時就會發現,自己的開發分支跟正常的穩定環境完全不影響。多環境時無需害怕環境,只需排查當前處於哪個環境,哪些環境是不需要訂閱的,那就將其去掉,換成所處的環境即可。
針對訂閱關系不一致的情況,可以去客戶端中查看訂閱關系哪裏不一致,哪個環境的topic或消費者不應該去訂閱,這時容易排查問題。
另外關於Docker,當消費者集起來時,該如何告訴Broker起了多少個實例,以此讓Broker來分配,哪些實例消費哪些partition或哪些Q。在consumer集起來時,可將訂閱關系還有能唯一標識是我的客戶端ID發給Broker,讓Broker決定哪個客戶端ID消費哪個隊列,進而做好分配,這時就能保證正常消費。
那如何標識我是我?這時有一個字符串,字符串有三個值組成,第一是消費者所在機器的IP,第二是at符號,第三是instance name,即實例名。
之前的配置是一個consumer中的實例名是相同的,如果是Docker環境,取出的IP也是相同的。假設有10臺機器在Docker裏集起來,當將消息匯報上去之後,Broker在拿到ID去重後發現只有一臺機器啟起來,它便將所有的隊列只分配給這一臺機器,但這臺機器很隨機,其它機器便收不到消息。此時本來有10臺機器,結果只有一臺機器收到消息,或會重復收到消息,結果就會很棘手。
為解決上述問題,在配置時,可去掉消費者的instance name,這時它會變成一串數字,即當前進程啟起來的進程號。大部分情況,容器中啟起來的進程號,我們認為是不一樣的,但也有可能是相同的,這時可以重啟一下。但這種概率比較小,如果有兩套集群就有可能了,這樣就解決了Docker的問題。
接下來簡單解釋一下RocketMQ裏面的訂閱關系,一條訂閱關系指一個消費者組訂閱了一個topic的某一個tag,三者合起來稱為一個訂閱關系。
關於tag,指topic的一個子分類,topic是對消息邏輯的歸類,我可以為某一些消息打上特定的tag,這樣做的益處是會做一些服務端過濾,假設只訂閱某個tag消息,那就不會發送其它tag消息給你,中間沒有數據流量的,這對於網絡輿論而言會減少很大一部分。
另外,我們做了簡單的MQ雲平臺,可以理解為與阿裏雲平臺是類似的,用戶有租戶隔離,不同的租戶之間看不到彼此的數據,所有的管理維度是通過APP的維度。
簡單介紹下,一個應用有應用的owner,應用的member,這些人可以管理APP中的所有資源,包括APP中的生產者組,topic、消費者組,通過這些可以完成以下事情:
一位研發同學如果想與另外一個團隊進行消息交互,作為生產消息的人,可以去平臺申請個topic,進而發消息將topic告訴消費者。比如,我的topic是這個,你去消費吧。在拿到之後,再去創建一個消費者組,訂閱這個topic,全程無人幹預,目前需要的幹預是我們會做一層審核。
現在很多人不是很熟悉,所以需要引導一段時間後,再將權限完全開放出來,屆時審計的工作就給應用的owner即可。
應用的owner通常是團隊的leader,這樣就可以在自己的小組中做到完全自助。對研發團隊而言,可以省去許多繁雜流程。
目前整套MQ的工作過程是如果需要資源,先去VKMQ平臺申請資源,比如我是生產者,這時需要一個topic,一個生產者組來生產消息,此時可以在自己創建之後用平臺客戶端發,無需擔心到底需要發到哪個Broker,哪個name server。
這時研發同學用起來會比較方便,不用關心需要哪些集群。如果沒有這樣的發現機制,可能還需要寫Broker地址或name server地址。
How about the future
提到以後可能的發展走向,之前與阿裏中間件技術團隊專家王昕聊過這個問題,了解到會有OpenMessaging,OpenMessaging的目的是有一套公開的標準,是MQ層面的標準,會定義一些基本操作。就現在情況而言,個人覺得,這三者可以結合到一起。
目前我們有用Kafka和RocketMQ。這時該如何管理,如何統一,對人的要求就很高。
但具體處理問題時會有差異,我可以大膽猜測一下,可以將兩個可能合而為一,依循OpenMessaging的標準。既然有一套比較好的標準可以遵循,如果能實現統一,我覺得對後來的人就會很方便。
技術幹貨丨如何在VIPKID中構建MQ服務