聊聊我與流式計算的故事

2023-01-21 06:00:35

聊聊流式計算吧 , 那一段經歷於我而言很精彩,很有趣,想把這段經歷分享給大家。

1 背景介紹

2014年,我在藝龍旅行網促銷團隊負責紅包系統。

彼時,促銷大戰如火如荼,優惠券計算服務也成為藝龍促銷業務中最重要的服務之一。

而優惠券計算服務正是採用當時大名鼎鼎的流式計算框架 Storm

流式計算是利用分散式的思想和方法,對海量「流」式資料進行實時處理的系統,它源自對海量資料「時效」價值上的挖掘訴求。

優惠券計算服務的邏輯是:每個城市每個酒店的使用優惠券的規則並不相同,當運營人員修改規則之後,觸發優惠券計算服務,計算完成之後,使用者下單時在使用優惠券時會呈現最新的規則。

優惠券計算服務是我們團隊的明星專案,很多研發的同學都對 Storm 特別感興趣 , 因為 Storm 的核心開發語言是 clojure , 比較小眾。

於是,在團隊內部,發現一個很有趣的現象:很多同學的辦公桌上放著《clojure in Action 》這本書

彼時,藝龍開始發力行動網際網路,業務量的激增,優惠券計算服務開始遇到了瓶頸。

比如運營人員修改全量規則時,整個計算流程要耗時一上午,也就談不上實時計算了。

CTO 幾次找團隊負責人,並嚴厲批責成他儘快優化。經過一個半月幾次優化,系統的瓶頸依然明顯,時不時運營同事會走到我們的工位附近,催促我們:「系統生效了麼? 」

我並不負責計算服務,每當同事被質疑時,我都感到很疑惑:「優惠券計算服務真的那麼複雜嗎? 」 , 同時也躍躍欲試:「 Storm 真有那麼難搞嗎?」

我心中暗暗下定了決心,一定要弄清楚優惠券計算服務的邏輯 。

2 國圖學習

北京有很多景點都讓我流連忘返,比如史鐵生小說裡的地壇,滿山楓葉的香山,如詩如畫的頤和園,美侖美奐的天壇 。

在我心裡,有一處很神聖的地方,它是知識和希望的象徵,那就是國家圖書館

中國國家圖書館位於北京市中關村南大街33號,與海淀區白石橋高粱河、紫竹院公園相鄰。它是國家總書庫,國家書目中心,國家古籍保護中心,同時也是世界最大、最先進的國家圖書館之一

每到週末,當我想安靜下來,專注思考時,我就會揹著筆記型電腦來到國家圖書館。

選擇自己喜歡的書,然後將筆記型電腦開啟,一邊看書,一邊在電腦上寫點筆記。

偶爾擡起頭,望著那些正在閱讀的讀者,心裡面感覺很陽關,覺得生命充滿了希望。

我並不負責流式計算服務,但想要揭開 Storm 神祕面紗的探索欲,同時探尋優惠券計算服務為什麼會這麼慢的渴望,讓我好幾天晚上沒睡好。

於是週六上午9點半, 我來到國家圖書館 ,想讓自己安靜下來,思考如何解決這個問題。解決問題的快感,是我一直追求的。

當我把筆記型電腦放平在桌上,我很興奮,同時靈臺一片澄清:優惠券計算服務的核心是 Storm ,那麼我需要先了解 Storm 的整體概念

開啟官網,瀏覽官網的檔案,第一次看到 Storm 的邏輯流程圖時, 做為程式設計師,我第一次竟然感受到抽象之美:從源頭流下來的水通過水龍頭( Spout ),再經過層層轉接頭( Bolt )過濾,不就是我們想要的純淨水嗎?

其實我們原來都是 CRUD boy ,機械的使用那些框架,只會做增刪改查,並不會思考框架背後的設計思路。 但框架到底是什麼?從來沒有思考過。 我一直覺得我很笨拙,學什麼都很慢,但那一刻我突然恍然大悟:框架本身是將解決問題的思路抽象化,從而便於研發人員使用,把複雜的問題抽象成有美感,是需要功底的。

瞭解完 Storm 整體概念 , 下一步也就是大家熟知的寫 Hello World 階段了 。

我參考教學寫了一個簡單的 Storm 應用(簡稱:拓撲),在部署後,程式正常跑了起來。

我腦海裡一直有一個疑問:「是不是優惠券計算服務的 storm 叢集的設定沒有調優,才導致計算的效能太差 ? 」 所以我必須去理解 storm 的並行度是如何計算的

整個下午,我一直在查閱相關的資料,並結合下圖思考:Nimbus, Supervisor ,Worker ,Task 這些名詞到底是什麼概念,以及他們之間是如何互動的。

進而思考:拓撲到底會啟動幾個程序,每個程序內部執行緒模型是怎樣的,頗有些庖丁解牛的味道。

這個習慣一直保持到現在,當我看到一個系統,我會下意思的去思考:「這個系統的執行緒模型如何,每次操作有哪些執行緒參於,他們之間如何互動」。我知道有更厲害的大牛,執行一行程式碼就知道 CPU 會執行的哪些指令,我做不到,但我覺得那就更加深刻了。

不管怎樣,這一天,我的思緒經過多次的變化,興奮,猶疑,放棄,陽光,激動,畏難心理一直存在,很多次想放棄,但好奇心一直鼓勵著我。

等天色已黑,我走出國圖的大門,腦子裡全部都是 Storm 程序,執行緒模型,內心裡面,有了莫名的自信。感覺自己就像仙劍奇俠傳裡的酒劍仙,伴隨著激昂的 BGM ,拔劍四顧,斬妖除魔。

御劍乘風來,除魔天地間,有酒樂逍遙,無酒我亦癲。

一飲盡江河,再飲吞日月,千杯醉不倒,唯我酒劍仙。

3 找到瓶頸

當我理解了 Storm 的整體概念,接下來我需要去找到優惠券計算服務的效能瓶頸。這個時候,梳理計算服務整體流程非常關鍵。

計算服務整體流程分為三個步驟 :

  1. 抽取資料:酒店資訊拉取服務拉取酒店資訊,並儲存到水源頭( Redis A/B 叢集 ) ;
  2. 計算過程:Storm 拓撲從水源頭獲取酒店資料,通過運營設定的規則對資料進行清洗 ,將計算好的資料儲存到水存放池 ( Redis C 叢集) ;
  3. 入庫階段:入庫服務水存放池獲取資料,將計算結果儲存到資料庫 。

當我們把整個計算的過程拆分成 抽取-->計算 --> 儲存 三個階段的時候,計算服務的架構就變得異常清晰,那到底在哪個階段最耗時 ,也成為我追查的目標。

優惠券計算服務當時沒有詳細的效能監控體系,所以我只能先從紀錄檔著手。 在運營同事觸發全量計算後,分別觀察三個階段對應服務的紀錄檔:

  • 抽取資料:酒店資訊拉取服務
  • 計算過程: Storm 拓撲
  • 入庫階段: 入庫服務

令人驚訝的現象:一次全量計算需要耗時4個多小時,但抽取資料的任務竟然跑了2個多小時,和我預期完全不一樣。

假如我把酒店資訊拉取服務比作抽水泵,那麼整個系統最大的問題竟然是抽水泵抽水馬力不足

4 推進重構

為什麼抽水泵抽水馬力不足 ?

通過閱讀原始碼,我發現因為執行緒模型不夠好,應用在部署多個節點後,每個節點只能有兩個執行緒執行拉取酒店資訊。

怎麼處理呢? 在原有程式碼上優化可行嗎? 好像也不太容易,因為老程式碼最初是一個 C# 研發同事寫的,他當時也不熟悉 JAVA ,從設計層面來講,有很多冗餘且不合理的程式碼,而且經過3年左右的維護,程式碼老化嚴重,於是我只能想到重構。

當我把想法和團隊負責人溝通後,他有點半信半疑,他認為我的判斷沒有問題,但不確定我是否可以將系統重構好。 我那時候信心爆棚,主動請纓,打包票不會出問題的。可能是由於 CTO 逼的太緊了,他同意了。

在重構之間,梳理好系統的整體邏輯。

重構的重點原則有兩條:

  1. 拉取服務可水平擴充套件,若效能不足時,增加服務節點即可提升效能;

  2. 組態檔可設定 worker 執行緒數量。

那思想層面,我已經做好準備了,那硬實力層面我有沒有做好準備嗎? 非常自信的講,準備好了,因為我遇到了 RocketMQ

我在《我與訊息佇列的8年情緣》這篇文章寫到:

2014年,我搜羅了很多的淘寶的訊息佇列的資料,我知道MetaQ的版本已經升級MetaQ 3.0,只是開源版本還沒有放出來。

大約秋天的樣子,我加入了RocketMQ技術群。誓嘉(RocketMQ創始人)在群裡說:「最近要開源了,放出來後,大家趕緊fork呀」。他的這句話發在群裡之後,群裡都炸開了鍋。我更是歡喜雀躍,期待著能早日見到阿里自己內部的訊息中介軟體。

終於,RocketMQ終於開源了。我迫不及待想一窺他的風采。

因為我想學網路程式設計,而RocketMQ的通訊模組remoting底層也是Netty寫的。所以,RocketMQ的通訊層是我學習切入的點。

我模仿RocketMQ的remoting寫了一個玩具的rpc,這更大大提高我的自信心。正好,藝龍舉辦技術創新活動。我想想,要不嘗試一下用Netty改寫下Cobar的通訊模組。於是參考Cobar的原始碼花了兩週寫了個netty版的proxy,其實非常粗糙,很多功能不完善。後來,這次活動頒給我一個鼓勵獎,現在想想都很好玩。

在重構酒店資訊拉取服務時,我將 RocketMQ 如何建立執行緒的知識點正好也用了上去,並學習如何將模組拆分得更加合理。同時在重構過程中,不斷 Review 新老程式碼的差別,確保核心邏輯正確。

非常幸運,大概一週時間,我就重構完了。

重構完成並不意味著結束,怎麼驗證呢 ? 我當時採取了兩種方式:

  • 程式碼評審

    我拉著優惠券計算服務的同事,一起 review 程式碼 。整個過程,大家也並沒有提出異議,並對我建立執行緒的技巧感到很好奇。我心中竊喜:」那是學習 RocketMQ 的「。

  • 測試環境資料驗證

    我們將新舊兩版服務同時觸發,比對兩個版本的資料的異同,將比對結果輸出到紀錄檔檔案,然後從中找到差異的地方,修復重構版的 BUG 。 然後在測試環境部署重構版,觀察一段時間,確保無異常。

從編寫第一行程式碼,三週時間,重構版終於上線了。 我將原來的老服務替換後,部署了3個節點, 每個節點8個 worker 並行拉取酒店資訊 。

令人開心和激動的是,重構是非常成功的。 因為業務給我們的時間需求也是1個小時左右。一次全量計算從原來4個小時急速縮減到1小時15分鐘,整個酒店拉取服務耗時40分鐘左右。

我心裡長舒一口氣,內心吟誦李白的詩:"十步殺一人,千里不留行。事了拂衣去,深藏身與名。"

5 向前一步

前 Facebook COO 謝麗爾·桑德伯格 寫了一本書《向前一步》,我特別喜歡這本書的書名 。

在優化優惠券計算服務的前期,團隊經過一個多月的時間,也沒有什麼成效。 我自己也猶豫:」我能不能解決這個問題?「 ,但最終我還是向前一步,並幫助團隊大大提升了服務的效能,負責人也有了信心,他也敢投入資源優化Storm 拓撲入庫流程

在閱讀優惠券計算服務的程式碼中,我發現兩個問題:

  1. 流式計算邏輯中有大量網路 IO 請求,主要是查詢特定的酒店資料,用於後續計算;
  2. 每次計算時需要查詢基礎設定資料,它們都是從資料庫中獲取。

對於Storm 拓撲優化,我提了兩點建議:

  1. 流式計算拓撲和酒店拉取服務各司其職,將流式計算中的網路 IO 請求挪到酒店拉取服務,將資料前置準備好;
  2. 基礎設定快取化,引入讀寫鎖(也是 RocketMQ 名字服務的技巧)。

對於入庫流程,一位研發同學將原來的單條資料入庫修改成批次入庫。

經過大家一起努力 ,優惠券計算服務的整體效能大大提升了,全量計算耗時已經變成40分鐘了,再也不會有運營同事在我們的工位附近吐槽系統慢了。

6 寫到最後

2014年,我向前一步推動了公司流式計算服務的優化,並取得了一點點進步。

時光荏苒,我已中年,生命中遇到越來越多的挫折,有的時候也會讓人低落,但每當想起這個故事,我會深深感動於當時的一往無前。

當再次面臨選擇時,我希望自己也能夠向前一步,想著如何幫助讀者成長,或是實現一個產品幫助更多的人。


如果我的文章對你有所幫助,還請幫忙點贊、在看、轉發一下,你的支援會激勵我輸出更高質量的文章,非常感謝!