我與消息隊列的八年情緣
本文轉(zhuǎn)載自微信公眾號「勇哥java實戰(zhàn)分享」,作者勇哥。轉(zhuǎn)載本文請聯(lián)系勇哥java實戰(zhàn)分享公眾號。
談起消息隊列,內(nèi)心還是會有些波瀾。
消息隊列,緩存,分庫分表是高并發(fā)解決方案三劍客,而消息隊列是我最喜歡,也是思考最多的技術(shù)。
我想按照下面的四個階段分享我與消息隊列的故事,同時也是對我技術(shù)成長經(jīng)歷的回顧。
- 初識:ActiveMQ
- 進階:Redis&RabbitMQ
- 升華:MetaQ
- 鐘情:RocketMQ
1 初識ActiveMQ
1.1 異步&解耦
2011年初,我在一家互聯(lián)網(wǎng)彩票公司做研發(fā)。
我負(fù)責(zé)的是用戶中心系統(tǒng),提供用戶注冊,查詢,修改等基礎(chǔ)功能。用戶注冊成功之后,需要給用戶發(fā)送短信。
因為原來都是面向過程編程,我就把新增用戶模塊和發(fā)送短信模塊都揉在一起了。
起初都還好,但問題慢慢的顯現(xiàn)出來。
- 短信渠道不夠穩(wěn)定,發(fā)送短信會達(dá)到5秒左右,這樣用戶注冊接口耗時很大,影響前端用戶體驗;
- 短信渠道接口發(fā)生變化,用戶中心代碼就必須修改了。但用戶中心是核心系統(tǒng)。每次上線都必要謹(jǐn)小慎微。這種感覺很別扭,非核心功能影響到核心系統(tǒng)了。
第一個問題,我可以采取線程池的方法來做,主要是異步化。但第二個問題卻讓我束手無措。
于是我向技術(shù)經(jīng)理請教,他告訴我引入消息隊列去解決這個問題。
- 將發(fā)送短信功能單獨拆成獨立的Job服務(wù);
- 用戶中心用戶注冊成功后,發(fā)送一條消息到消息隊列,Job服務(wù)收到消息調(diào)用短信服務(wù)發(fā)送短信即可。
這時,我才明白: 消息隊列最核心的功能就是異步和解耦。
1.2 調(diào)度中心
彩票系統(tǒng)的業(yè)務(wù)是比較復(fù)雜的。在彩票訂單的生命周期里,經(jīng)過創(chuàng)建,拆分子訂單,出票,算獎等諸多環(huán)節(jié)。每一個環(huán)節(jié)都需要不同的服務(wù)處理,每個系統(tǒng)都有自己獨立的表,業(yè)務(wù)功能也相對獨立。假如每個應(yīng)用都去修改訂單主表的信息,那就會相當(dāng)混亂了。
公司的架構(gòu)師設(shè)計了調(diào)度中心的服務(wù),調(diào)度中心的職責(zé)是維護訂單核心狀態(tài)機,訂單返獎流程,彩票核心數(shù)據(jù)生成。
調(diào)度中心通過消息隊列和出票網(wǎng)關(guān),算獎服務(wù)等系統(tǒng)傳遞和交換信息。
這種設(shè)計在那個時候青澀的我的眼里,簡直就是水滴vs人類艦隊,降維打擊。
隨著我對業(yè)務(wù)理解的不斷深入,我隱約覺得:“好的架構(gòu)是簡潔的,也是應(yīng)該易于維護的”。
當(dāng)彩票業(yè)務(wù)日均千萬交易額的時候,調(diào)度中心的研發(fā)維護人員也只有兩個人。調(diào)度中心的源碼里業(yè)務(wù)邏輯,日志,代碼規(guī)范都是極好的。
在我日后的程序人生里,我也會下意識模仿調(diào)度中心的編碼方式,“不玩奇技淫巧,代碼是給人閱讀的”。
1.3 重啟大法
隨著彩票業(yè)務(wù)的爆炸增長,每天的消息量從30萬激增到150~200萬左右,一切看起來似乎很平穩(wěn)。
某一天雙色球投注截止,調(diào)度中心無法從消息隊列中消費數(shù)據(jù)。消息總線處于只能發(fā),不能收的狀態(tài)下。整個技術(shù)團隊都處于極度的焦慮狀態(tài),“要是出不了票,那可是幾百萬的損失呀,要是用戶中了兩個雙色球?那可是千萬呀”。大家急得像熱鍋上的螞蟻。
這也是整個技術(shù)團隊第一次遇到消費堆積的情況,大家都沒有經(jīng)驗。
首先想到的是多部署幾臺調(diào)度中心服務(wù),部署完成之后,調(diào)度中心消費了幾千條消息后還是Hang住了。這時,架構(gòu)師只能采用重啟的策略。你沒有看錯,就是重啟大法。說起來真的很慚愧,但當(dāng)時真的只能采用這種方式。
調(diào)度中心重啟后,消費了一兩萬后又Hang住了。只能又重啟一次。來來回回持續(xù)20多次,像擠牙膏一樣。而且隨著出票截止時間的臨近,這種思想上的緊張和恐懼感更加強烈。終于,通過1小時的手工不斷重啟,消息終于消費完了。
我當(dāng)時正好在讀畢玄老師的《分布式j(luò)ava應(yīng)用基礎(chǔ)與實踐》,猜想是不是線程阻塞了,于是我用Jstack命令查看堆棧情況。果然不出所料,線程都阻塞在提交數(shù)據(jù)的方法上。
我們馬上和DBA溝通,發(fā)現(xiàn)oracle數(shù)據(jù)庫執(zhí)行了非常多的大事務(wù),每次大的事務(wù)執(zhí)行都需要30分鐘以上,導(dǎo)致調(diào)度中心的調(diào)度出票線程阻塞了。
技術(shù)部后來采取了如下的方案規(guī)避堆積問題:
生產(chǎn)者發(fā)送消息的時候,將超大的消息拆分成多批次的消息,減少調(diào)度中心執(zhí)行大事務(wù)的幾率;
數(shù)據(jù)源配置參數(shù),假如事務(wù)執(zhí)行超過一定時長,自動拋異常,回滾。
1.4 復(fù)盤
Spring封裝的ActiveMQ的API非常簡潔易用,使用過程中真的非常舒服。
受限于當(dāng)時彩票技術(shù)團隊的技術(shù)水平和視野,我們在使用ActiveMQ中遇到了一些問題。
高吞吐下,堆積到一定消息量易Hang住;
技術(shù)團隊發(fā)現(xiàn)在吞吐量特別高的場景下,假如消息堆積越大,ActiveMQ有較小幾率會Hang住的。
出票網(wǎng)關(guān)的消息量特別大,有的消息并不需要馬上消費,但是為了規(guī)避消息隊列Hang住的問題,出票網(wǎng)關(guān)消費數(shù)據(jù)的時候,先將消息先持久化到本地磁盤,生成本地XML文件,然后異步定時執(zhí)行消息。通過這種方式,我們大幅度提升了出票網(wǎng)關(guān)的消費速度,基本杜絕了出票網(wǎng)關(guān)隊列的堆積。
但這種方式感覺也挺怪的,消費消息的時候,還要本地再存儲一份數(shù)據(jù),消息存儲在本地,假如磁盤壞了,也有丟消息的風(fēng)險。
高可用機制待完善
我們采用的master/slave部署模式,一主一從,服務(wù)器配置是4核8G 。
這種部署方式可以同時運行兩個ActiveMQ, 只允許一個slave連接到Master上面,也就是說只能有2臺MQ做集群,這兩個服務(wù)之間有一個數(shù)據(jù)備份通道,利用這個通道Master向Slave單向地數(shù)據(jù)備份。這個方案在實際生產(chǎn)線上不方便, 因為當(dāng)Master掛了之后, Slave并不能自動地接收Client發(fā)來的請來,需要手動干預(yù),且要停止Slave再重啟Master才能恢復(fù)負(fù)載集群。
還有一些很詭異丟消息的事件,生產(chǎn)者發(fā)送消息成功,但master控制臺查詢不到,但slave控制臺竟然能查詢到該消息。
但消費者沒有辦法消費slave上的消息,還得通過人工介入的方式去處理。
2 進階Redis&RabbitMQ
2014年,我在藝龍網(wǎng)從事紅包系統(tǒng)和優(yōu)惠券系統(tǒng)優(yōu)化相關(guān)工作。
2.1 Redis可以做消息隊列嗎
酒店優(yōu)惠券計算服務(wù)使用的是初代流式計算框架Storm。Storm這里就不詳細(xì)介紹,可以參看下面的邏輯圖:
這里我們的Storm集群的水源頭(數(shù)據(jù)源)是redis集群,使用list數(shù)據(jù)結(jié)構(gòu)實現(xiàn)了消息隊列的push/pop功能。
流式計算的整體流程:
- 酒店信息服務(wù)發(fā)送酒店信息到Redis集群A/B;
- Storm的spout組件從Redis集群A/B獲取數(shù)據(jù), 獲取成功后,發(fā)送tuple消息給Bolt組件;
- Bolt組件收到消息后,通過運營配置的規(guī)則對數(shù)據(jù)進行清洗;
- 最后Storm把處理好的數(shù)據(jù)發(fā)送到Redis集群C;
- 入庫服務(wù)從Redis集群C獲取數(shù)據(jù),存儲數(shù)據(jù)到數(shù)據(jù)庫;
- 搜索團隊掃描數(shù)據(jù)庫表,生成索引。
storm說明
這套流式計算服務(wù)每天處理千萬條數(shù)據(jù),處理得還算順利。但方案在團隊內(nèi)部還是有不同聲音:
- storm的拓?fù)渖墪r候,或者優(yōu)惠券服務(wù)重啟的時候,偶爾出現(xiàn)丟消息的情況。但消息的丟失,對業(yè)務(wù)來講沒有那么敏感,而且我們也提供了手工刷新的功能,也在業(yè)務(wù)的容忍范圍內(nèi);
- 團隊需要經(jīng)常關(guān)注Redis的緩存使用量,擔(dān)心Redis隊列堆積, 導(dǎo)致out of memory;
- 架構(gòu)師認(rèn)為搜索團隊直接掃描數(shù)據(jù)庫不夠解耦,建議將Redis集群C替換成Kafka,搜索團隊從kafka直接消費消息,生成索引;
我認(rèn)為使用Redis做消息隊列應(yīng)該滿足如下條件:
- 容忍小概率消息丟失,通過定時任務(wù)/手工觸發(fā)達(dá)到最終一致的業(yè)務(wù)場景;
- 消息堆積概率低,有相關(guān)的報警監(jiān)控;
- 消費者的消費模型要足夠簡單。
2.2 RabbitMQ是管子不是池子
RabbitMQ是用erlang語言編寫的。RabbitMQ滿足了我的兩點需求:
高可用機制。藝龍內(nèi)部是使用的鏡像高可用模式,而且這種模式在藝龍已經(jīng)使用了較長時間了,穩(wěn)定性也得到了一定的驗證。
我負(fù)責(zé)的紅包系統(tǒng)里,RabbitMQ每天的吞吐也在百萬條消息左右,消息的發(fā)送和消費都還挺完美。
優(yōu)惠券服務(wù)原使用SqlServer,由于數(shù)據(jù)量太大,技術(shù)團隊決定使用分庫分表的策略,使用公司自主研發(fā)的分布式數(shù)據(jù)庫DDA。
因為是第一次使用分布式數(shù)據(jù)庫,為了測試DDA的穩(wěn)定性,我們模擬發(fā)送1000萬條消息到RabbitMQ,然后優(yōu)惠券重構(gòu)服務(wù)消費消息后,按照用戶編號hash到不同的mysql庫。
RabbitMQ集群模式是鏡像高可用,3臺服務(wù)器,每臺配置是4核8G 。
我們以每小時300萬條消息的速度發(fā)送消息,最開始1個小時生產(chǎn)者和消費者表現(xiàn)都很好,但由于消費者的速度跟不上生產(chǎn)者的速度,導(dǎo)致消息隊列有積壓情況產(chǎn)生。第三個小時,消息隊列已堆積了500多萬條消息了, 生產(chǎn)者發(fā)送消息的速度由最開始的2毫秒激增到500毫秒左右。RabbitMQ的控制臺已血濺當(dāng)場,標(biāo)紅報警。
這是一次無意中的測試,從測試的情況來看,RabbitMQ很優(yōu)秀,但RabbitMQ對消息堆積的支持并不好,當(dāng)大量消息積壓的時候,會導(dǎo)致 RabbitMQ 的性能急劇下降。
有的朋友對我講:“RabbitMQ明明是管子,你非得把他當(dāng)池子?”
隨著整個互聯(lián)網(wǎng)數(shù)據(jù)量的激增, 很多業(yè)務(wù)場景下是允許適當(dāng)堆積的,只要保證消費者可以平穩(wěn)消費,整個業(yè)務(wù)沒有大的波動即可。
我心里面越來越相信:消息隊列既可以做管子,也可以當(dāng)做池子。
3 升華MetaQ
Metamorphosis的起源是我從對linkedin的開源MQ–現(xiàn)在轉(zhuǎn)移到apache的kafka的學(xué)習(xí)開始的,這是一個設(shè)計很獨特的MQ系統(tǒng),它采用pull機制,而 不是一般MQ的push模型,它大量利用了zookeeper做服務(wù)發(fā)現(xiàn)和offset存儲,它的設(shè)計理念我非常欣賞并贊同,強烈建議你閱讀一下它的設(shè)計文檔,總體上說metamorphosis的設(shè)計跟它是完全一致的。--- MetaQ的作者莊曉丹
3.1 驚艷消費者模型
2015年,我主要從事神州專車訂單研發(fā)工作。
MetaQ滿足了我對于消息隊列的幻想:“分布式,高吞吐,高堆積”。
MetaQ支持兩種消費模型:集群消費和廣播消費 ,因為以前使用過的消費者模型都是用隊列模型,當(dāng)我第一次接觸到這種發(fā)布訂閱模型的時候還是被驚艷到了。
▍ 集群消費
訂單創(chuàng)建成功后,發(fā)送一條消息給MetaQ。這條消息可以被派單服務(wù)消費,也可以被BI服務(wù)消費。
▍ 廣播消費
派單服務(wù)在講訂單指派給司機的時候,會給司機發(fā)送一個推送消息。推送就是用廣播消費的模式實現(xiàn)的。
大體流程是:
- 司機端推送服務(wù)是一個TCP服務(wù),啟動后,采用的是廣播模式消費MetaQ的PushTopic;
- 司機端會定時發(fā)送TCP請求到推送服務(wù),鑒權(quán)成功后,推送服務(wù)會保存司機編號和channel的引用;
- 派單服務(wù)發(fā)送推送消息到MetaQ;
- 推送服務(wù)的每一臺機器都會收到該消息,然后判斷內(nèi)存中是否存在該司機的channel引用,若存在,則推送消息。
這是非常經(jīng)典的廣播消費的案例。我曾經(jīng)研讀京麥TCP網(wǎng)關(guān)的設(shè)計,它的推送也是采用類似的方式。
3.2 激進的消峰
2015年是打車大戰(zhàn)硝煙彌漫的一年。
對神州專車來講,隨著訂單量的不斷增長,欣喜的同時,性能的壓力與日俱增。早晚高峰期,用戶打車的時候,經(jīng)常點擊下單經(jīng)常無響應(yīng)。在系統(tǒng)層面來看,專車api網(wǎng)關(guān)發(fā)現(xiàn)大規(guī)模超時,訂單服務(wù)的性能急劇下降。數(shù)據(jù)庫層面壓力更大,高峰期一條記錄插入竟然需要8秒的時間。
整個技術(shù)團隊需要盡快提升專車系統(tǒng)的性能,此前已經(jīng)按照模塊領(lǐng)域做了數(shù)據(jù)庫的拆分。但系統(tǒng)的瓶頸依然很明顯。
我們設(shè)計了現(xiàn)在看來有點激進的方案:
設(shè)計訂單緩存。緩存方案大家要有興趣,我們可以以后再聊,里面有很多可以詳聊的點;
在訂單的載客生命周期里,訂單的修改操作先修改緩存,然后發(fā)送消息到MetaQ,訂單落盤服務(wù)消費消息,并判斷訂單信息是否正常(比如有無亂序),若訂單數(shù)據(jù)無誤,則存儲到數(shù)據(jù)庫中。
這里有兩個細(xì)節(jié):
消費者消費的時候需要順序消費,實現(xiàn)的方式是按照訂單號路由到不同的partition,同一個訂單號的消息,每次都發(fā)到同一個partition;
一個守護任務(wù),定時輪詢當(dāng)前正在進行的訂單,當(dāng)緩存與數(shù)據(jù)不一致時候,修復(fù)數(shù)據(jù),并發(fā)送報警。
這次優(yōu)化大大提升訂單服務(wù)的整體性能,也為后來訂單服務(wù)庫分庫分表以及異構(gòu)打下了堅實的基礎(chǔ),根據(jù)我們的統(tǒng)計數(shù)據(jù),基本沒有發(fā)生過緩存和數(shù)據(jù)庫最后不一致的場景。但這種方案對緩存高可用有較高的要求,還是有點小激進吧。
3.3 消息SDK封裝
做過基礎(chǔ)架構(gòu)的同學(xué)可能都有經(jīng)驗:“三方組件會封裝一層”,神州架構(gòu)團隊也是將metaq-client封裝了一層。
在我的思維里面,封裝一層可以減少研發(fā)人員使用第三方組件的心智投入,統(tǒng)一技術(shù)棧,也就如此了。
直到發(fā)生一次意外,我的思維升級了。那是一天下午,整個專車服務(wù)崩潰較長時間。技術(shù)團隊發(fā)現(xiàn):"專車使用zookeeper做服務(wù)發(fā)現(xiàn)。zk集群的leader機器掛掉了,一直在選主。"
臨時解決后,我們發(fā)現(xiàn)MetaQ和服務(wù)發(fā)現(xiàn)都使用同一套zk集群,而且consumer的offset提交,以及負(fù)載均衡都會對zk集群進行大量的寫操作。
為了減少MetaQ對zk集群的影響,我們的目標(biāo)是:“MetaQ使用獨立的zk集群”。
- 需要部署新的zk集群;
- MetaQ的zk數(shù)據(jù)需要同步到新的集群;
- 保證切換到新的集群,應(yīng)用服務(wù)基本無感知。
我很好奇向架構(gòu)部同學(xué)請教,他說新的集群已經(jīng)部署好了,但需要同步zk數(shù)據(jù)到新的集群。他在客戶端里添加了雙寫的操作。也就是說:我們除了會寫原有的zk集群一份數(shù)據(jù),同時也會在新的zk集群寫一份。過了幾周后,MetaQ使用獨立的zk集群這個任務(wù)已經(jīng)完成了。
這一次的經(jīng)歷帶給我很大的感慨:“還可以這么玩?” ,也讓我思考著:三方組件封裝沒有想像中那么簡單。
我們可以看下快手消息的SDK封裝策略:
- 對外只提供最基本的 API,所有訪問必須經(jīng)過SDK提供的接口。簡潔的 API 就像冰山的一個角,除了對外的簡單接口,下面所有的東西都可以升級更換,而不會破壞兼容性 ;
- 業(yè)務(wù)開發(fā)起來也很簡單,只要需要提供 Topic(全局唯一)和 Group 就可以生產(chǎn)和消費,不用提供環(huán)境、NameServer 地址等。SDK 內(nèi)部會根據(jù) Topic 解析出集群 NameServer 的地址,然后連接相應(yīng)的集群。生產(chǎn)環(huán)境和測試環(huán)境環(huán)境會解析出不同的地址,從而實現(xiàn)了隔離;
- 上圖分為 3 層,第二層是通用的,第三層才對應(yīng)具體的 MQ 實現(xiàn),因此,理論上可以更換為其它消息中間件,而客戶端程序不需要修改;
- SDK 內(nèi)部集成了熱變更機制,可以在不重啟 Client 的情況下做動態(tài)配置,比如下發(fā)路由策略(更換集群 NameServer 的地址,或者連接到別的集群去),Client 的線程數(shù)、超時時間等。通過 Maven 強制更新機制,可以保證業(yè)務(wù)使用的 SDK 基本上是最新的。
3.4 重構(gòu)MetaQ , 自成體系
我有一個習(xí)慣 : "經(jīng)常找運維,DBA,架構(gòu)師了解當(dāng)前系統(tǒng)是否有什么問題,以及他們解決問題的思路。這樣,我就有另外一個視角來審視公司的系統(tǒng)運行情況"。
MetaQ也有他的缺點。
MetaQ的基層通訊框架是gecko,MetaQ偶爾會出現(xiàn)rpc無響應(yīng),應(yīng)用假死的情況,不太好定位問題;
MetaQ的運維能力薄弱,只有簡單的Dashboard界面,無法實現(xiàn)自動化主題申請,消息追蹤等功能。
有一天,我發(fā)現(xiàn)測試環(huán)境的一臺消費者服務(wù)器啟動后,不斷報鏈接異常的問題,而且cpu占用很高。我用netstat命令馬上查一下,發(fā)現(xiàn)已經(jīng)創(chuàng)建了幾百個鏈接。出于好奇心,我打開了源碼,發(fā)現(xiàn)網(wǎng)絡(luò)通訊框架gecko已經(jīng)被替換成了netty。我們馬上和架構(gòu)部的同學(xué)聯(lián)系。
我這才明白:他們已經(jīng)開始重構(gòu)MetaQ了。我從來沒有想過重構(gòu)一個開源軟件,因為距離我太遠(yuǎn)了?;蛘吣莻€時候,我覺得自己的能力還達(dá)不到。
后來,神州自研的消息隊列自成體系了,已經(jīng)在生產(chǎn)環(huán)境運行的挺好。
時至今天,我還是很欣賞神州架構(gòu)團隊。他們自研了消息隊列,DataLink(數(shù)據(jù)異構(gòu)中間件),分庫分表中間件等。他們愿意去創(chuàng)新,有勇氣去做一個更好的技術(shù)產(chǎn)品。
我從他們身上學(xué)到很多。
也許在看到他們重構(gòu)MetaQ的那一刻,我的心里埋下了種子。
4 鐘情RocketMQ
4.1 開源的盛宴
2014年,我搜羅了很多的淘寶的消息隊列的資料,我知道MetaQ的版本已經(jīng)升級MetaQ 3.0,只是開源版本還沒有放出來。
大約秋天的樣子,我加入了RocketMQ技術(shù)群。誓嘉(RocketMQ創(chuàng)始人)在群里說:“最近要開源了,放出來后,大家趕緊fork呀”。他的這句話發(fā)在群里之后,群里都炸開了鍋。我更是歡喜雀躍,期待著能早日見到阿里自己內(nèi)部的消息中間件。
終于,RocketMQ終于開源了。我迫不及待想一窺他的風(fēng)采。
因為我想學(xué)網(wǎng)絡(luò)編程,而RocketMQ的通訊模塊remoting底層也是Netty寫的。所以,RocketMQ的通訊層是我學(xué)習(xí)切入的點。
我模仿RocketMQ的remoting寫了一個玩具的rpc,這更大大提高我的自信心。正好,藝龍舉辦技術(shù)創(chuàng)新活動。我想想,要不嘗試一下用Netty改寫下Cobar的通訊模塊。于是參考Cobar的源碼花了兩周寫了個netty版的proxy,其實非常粗糙,很多功能不完善。后來,這次活動頒給我一個鼓勵獎,現(xiàn)在想想都很好玩。
因為在神州優(yōu)車使用MetaQ的關(guān)系,我學(xué)習(xí)RocketMQ也比較得心應(yīng)手。為了真正去理解源碼,我時常會參考RocketMQ的源碼,寫一些輪子來驗證我的學(xué)習(xí)效果。
雖然自己做了一些練習(xí),但一直沒有在業(yè)務(wù)環(huán)境使用過。2018年是我真正使用RocketMQ的一年,也是有所得的一年。
▍ 短信服務(wù)
短信服務(wù)應(yīng)用很廣泛,比如用戶注冊登錄驗證碼,營銷短信,下單成功短信通知等等。最開始設(shè)計短信服務(wù)的時候,我想學(xué)習(xí)業(yè)界是怎么做的。于是把目標(biāo)鎖定在騰訊云的短信服務(wù)上。騰訊云的短信服務(wù)有如下特點:
- 統(tǒng)一的SDK,后端入口是http/https服務(wù) , 分配appId/appSecret鑒權(quán);
- 簡潔的API設(shè)計:單發(fā),群發(fā),營銷單發(fā),營銷群發(fā),模板單發(fā),模板群發(fā)。
于是,我參考了這種設(shè)計思路。
- 模仿騰訊云的SDK設(shè)計,提供簡單易用的短信接口;
- 設(shè)計短信服務(wù)API端,接收發(fā)短信請求,發(fā)送短信信息到消息隊列;
- worker服務(wù)消費消息,按照負(fù)載均衡的算法,調(diào)用不同渠道商的短信接口;
- Dashboard可以查看短信發(fā)送記錄,配置渠道商信息。
短信服務(wù)是我真正意義第一次生產(chǎn)環(huán)境使用RocketMQ,當(dāng)短信一條條發(fā)出來的時候,還是蠻有成就感的。
▍ MQ控制臺
使用過RocketMQ的朋友,肯定對上圖的控制臺很熟悉。當(dāng)時團隊有多個RocketMQ集群,每組集群都需要單獨部署一套控制臺。于是我想著:能不能稍微把控制臺改造一番,能滿足支持多組集群。
于是,擼起袖子干了起來。大概花了20天的時間,我們基于開源的版本改造了能支持多組集群的版本。做完之后,雖然能滿足我最初的想法,但是做的很粗糙。而且搜狐開源了他們自己的MQCloud ,我看了他們的設(shè)計之后, 覺得離一個消息治理平臺還很遠(yuǎn)。
后來我讀了《網(wǎng)易云音樂的消息隊列改造之路》,《今日頭條在消息服務(wù)平臺和容災(zāi)體系建設(shè)方面的實踐與思考》這兩篇文章,越是心癢難耐,蠻想去做的是一個真正意義上的消息治理平臺。一直沒有什么場景和機會,還是有點可惜。
最近看了哈羅單車架構(gòu)專家梁勇的一篇文章《哈啰在分布式消息治理和微服務(wù)治理中的實踐》,推薦大家一讀。
https://mp.weixin.qq.com/s/N-vd6he4nsZp-G3Plc4m6A
▍ 一扇窗子,開始自研組件
后來,我嘗試進一步深入使用RocketMQ。
- 仿ONS風(fēng)格封裝消息SDK;
- 運維側(cè)平滑擴容消息隊列;
- 生產(chǎn)環(huán)境DefaultMQPullConsumer消費模式嘗試
這些做完之后,我們又自研了注冊中心、配置中心,任務(wù)調(diào)度系統(tǒng)。設(shè)計這些系統(tǒng)的時候,從RocketMQ源碼里汲取了很多的營養(yǎng),雖然現(xiàn)在看來有很多設(shè)計不完善的地方,代碼質(zhì)量也有待提高,但做完這些系統(tǒng)后,還是大大提升我的自信心。
RocketMQ給我打開了一扇窗子,讓我能看到更廣闊的Java世界。對我而言,這就是開源的盛宴。
4.2 Kafka: 大數(shù)據(jù)生態(tài)的不可或缺的部分
Kafka是一個擁有高吞吐、可持久化、可水平擴展,支持流式數(shù)據(jù)處理等多種特性的分布式消息流處理中間件,采用分布式消息發(fā)布與訂閱機制,在日志收集、流式數(shù)據(jù)傳輸、在線/離線系統(tǒng)分析、實時監(jiān)控等領(lǐng)域有廣泛的應(yīng)用。
▍ 日志同步
在大型業(yè)務(wù)系統(tǒng)設(shè)計中,為了快速定位問題,全鏈路追蹤日志,以及故障及時預(yù)警監(jiān)控,通常需要將各系統(tǒng)應(yīng)用的日志集中分析處理。
Kafka設(shè)計初衷就是為了應(yīng)對大量日志傳輸場景,應(yīng)用通過可靠異步方式將日志消息同步到消息服務(wù),再通過其他組件對日志做實時或離線分析,也可用于關(guān)鍵日志信息收集進行應(yīng)用監(jiān)控。
日志同步主要有三個關(guān)鍵部分:日志采集客戶端,Kafka消息隊列以及后端的日志處理應(yīng)用。
日志采集客戶端,負(fù)責(zé)用戶各類應(yīng)用服務(wù)的日志數(shù)據(jù)采集,以消息方式將日志“批量”“異步”發(fā)送Kafka客戶端。Kafka客戶端批量提交和壓縮消息,對應(yīng)用服務(wù)的性能影響非常小。
Kafka將日志存儲在消息文件中,提供持久化。
日志處理應(yīng)用,如Logstash,訂閱并消費Kafka中的日志消息,最終供文件搜索服務(wù)檢索日志,或者由Kafka將消息傳遞給Hadoop等其他大數(shù)據(jù)應(yīng)用系統(tǒng)化存儲與分析。
日志同步示意圖:
▍流計算處理
在很多領(lǐng)域,如股市走向分析、氣象數(shù)據(jù)測控、網(wǎng)站用戶行為分析,由于數(shù)據(jù)產(chǎn)生快、實時性強且量大,您很難統(tǒng)一采集這些數(shù)據(jù)并將其入庫存儲后再做處理,這便導(dǎo)致傳統(tǒng)的數(shù)據(jù)處理架構(gòu)不能滿足需求。Kafka以及Storm、Samza、Spark等流計算引擎的出現(xiàn),就是為了更好地解決這類數(shù)據(jù)在處理過程中遇到的問題,流計算模型能實現(xiàn)在數(shù)據(jù)流動的過程中對數(shù)據(jù)進行實時地捕捉和處理,并根據(jù)業(yè)務(wù)需求進行計算分析,最終把結(jié)果保存或者分發(fā)給需要的組件。
▍ 數(shù)據(jù)中轉(zhuǎn)樞紐
近10多年來,諸如KV存儲(HBase)、搜索(ElasticSearch)、流式處理(Storm、Spark、Samza)、時序數(shù)據(jù)庫(OpenTSDB)等專用系統(tǒng)應(yīng)運而生。這些系統(tǒng)是為單一的目標(biāo)而產(chǎn)生的,因其簡單性使得在商業(yè)硬件上構(gòu)建分布式系統(tǒng)變得更加容易且性價比更高。通常,同一份數(shù)據(jù)集需要被注入到多個專用系統(tǒng)內(nèi)。例如,當(dāng)應(yīng)用日志用于離線日志分析時,搜索單個日志記錄同樣不可或缺,而構(gòu)建各自獨立的工作流來采集每種類型的數(shù)據(jù)再導(dǎo)入到各自的專用系統(tǒng)顯然不切實際,利用消息隊列Kafka版作為數(shù)據(jù)中轉(zhuǎn)樞紐,同份數(shù)據(jù)可以被導(dǎo)入到不同專用系統(tǒng)中。
下圖是美團 MySQL 數(shù)據(jù)實時同步到 Hive 的架構(gòu)圖,也是一個非常經(jīng)典的案例。
4.3 如何技術(shù)選型
2018年去哪兒QMQ開源了,2019年騰訊TubeMQ開源了,2020年P(guān)ulsar如火如荼。
消息隊列的生態(tài)是如此的繁榮,那我們?nèi)绾芜x型呢?
我想我們不必局限于消息隊列,可以再擴大一下。簡單談一談我的看法。
Databases are specializing – the “one size fits all” approach no longer applies ----- MongoDB設(shè)計哲學(xué)
第一點:先有場景,然后再有適配這種場景的技術(shù)。什么樣的場景選擇什么樣的技術(shù)。
第二點:現(xiàn)實往往很復(fù)雜,當(dāng)我們真正做技術(shù)選型,并需要落地的時候,技術(shù)儲備和成本是兩個我們需要重點考量的因素。
▍ 技術(shù)儲備
技術(shù)團隊有無使用這門技術(shù)的經(jīng)驗,是否踩過生產(chǎn)環(huán)境的坑,以及針對這些坑有沒有完備的解決方案;
架構(gòu)團隊是否有成熟的SDK,工具鏈,甚至是技術(shù)產(chǎn)品。
▍ 成本
- 研發(fā),測試,運維投入人力成本;
- 服務(wù)器資源成本;
- 招聘成本等。
最后一點是人的因素,特別是管理者的因素。每一次大的技術(shù)選型考驗技術(shù)管理者的視野,格局,以及管理智慧。
5 寫到最后
我覺得這個世界上沒有什么毫無道理的橫空出世,真的,如果沒有大量的積累大量的思考是不會把事情做好的。。。總之,在經(jīng)歷了這部電影以后,我覺得我要學(xué)的太多了,這世界上有太多的能人,你以為的極限,弄不好,只是別人的起點。所以只有不停地進取,才能不丟人。那,人可以不上學(xué),但一定要學(xué)習(xí),真的。------ 韓寒《后會無期》演講
我學(xué)習(xí)消息隊列的過程是不斷思考,不斷實踐的過程,雖然我以為的極限,弄不好,只是別人的起點,但至少現(xiàn)在,當(dāng)我面對這門技術(shù)的時候,我的內(nèi)心充滿了好奇心,同時也是無所畏懼的。
我始終相信:每天學(xué)習(xí)一點點,比昨天進步一點點就好。