經驗貼 | 如何從業務實際需求出發,參與 5.9K star 的 Node.js 開源專案

語言: CN / TW / HK

theme: channing-cyan

軟體正在吞噬世界,而開源正在吞噬軟體

前言

開源對於軟體生態的意義已經人盡皆知。如何參與開源,也成為很多“開源”新手最關注的問題。本文旨在記錄作者從使用autocannon到為autocannonPull Request並被Merge的過程。從一個真實案例出發,向大家介紹:如何從業務實際需求,反哺開源生態?

本文相關的PR地址:https://github.com/mcollina/autocannon/pull/443

背景

最近團隊在做服務端 SSR 框架的升級。對於升級工作來說,如何量化升級前後的效能提升資料是非常重要的部分,也是衡量我們工作成果的最有效的手段。

為此,我對市面上流行的壓測方案進行了一些考察比較,autocannon從使用方式、可定製化、細粒度的結果指標等多個維度都很滿足我們的需要。最重要的是還處於活躍維護狀態。於是我們基於autocannon封裝了一個團隊內部的壓測工具。

image.png

同時可以看到,autocannonREADME.md的第一句就清晰的寫著:written in node。這對於前端工程師來說意味著它不是黑盒,我們可以去深入它、瞭解它,從而改變它。

所以接下來,我們就從發現問題、確認問題、解決問題這幾個部分來拆解如何深入瞭解並改變這個written in node的小東西。

發現問題

用內部基於autocannon封裝的工具進行幾次壓測之後,我發現了第一個問題:autocannon預設情況下把由於壓力過大而返回的兜底頁面也當做正確返回來統計了,因為此時的狀態碼依然是200

而從實際需求角度出發,這部分應該算作異常返回。對於我們的場景來說,正確返回的判斷條件應該是 正確的狀態碼 + 正確的頁面內容

於是我在文件中仔細檢視,找到了一個對返回內容校驗的引數expectBody,通過引數解釋我們可以看到這個引數可以對返回內容做一次equal判斷,不匹配則統計到錯誤資料中。這正是我們需要的!

image.png

正準備開始試驗這個引數時,我發現了第二個問題:返回體是很大的一段HTML,加到配置檔案中可讀性很差,同時每次請求都會有一些隨機的內容。也就是說每次壓測請求的返回體都不是完全一致的,都會有一些差別,但它們都是正確的。

從上面這個問題來看,在我們的業務場景下,這個引數是不能用的,而文件中並沒有提供其他類似的引數。

我們的壓測工作陷入了短暫的僵局。

確認問題

既然發現了開源專案的問題,我們此時就需要確定一下幾點: 1. 這個問題是不是隻有我們的業務場景才出現,對於普遍使用者來說是偽需求?如果是,請到方案一;如果不是,請看 2 2. 既然是普遍需求,那麼之前有沒有人提過?專案官方是否已經在安排解決?如果是,請到方案二;如果不是,請到方案三

下面三個解決方案:

  • 方案一:極度個性的需求請使用patch-package方案,在自己的專案進行修改(詳情請點選連結進一步瞭解)
  • 方案二:普遍需求,並且官方已經安排解決。那麼就去相關issue下積極回覆,描述自己遇到的問題,催促官方儘快解決併發布新版
  • 方案三:普遍需求,並之前沒有人提過issue,那麼我來提issue去跟官方溝通

再回到我們的實際場景,對於壓測返回內容的正確性驗證,每個業務可能都有自己的邏輯,單純的完全匹配肯定是不滿足需要的。所以,這是一個普遍需求

接下來我們要看官方是否有安排修改,這就需要我們issue中去查詢搜尋關鍵詞

image.png

通過對body關鍵詞的過濾,我們發現相關的issue或討論的不是我們的問題,或者在一年以前提過類似的問題但沒有進一步跟進。

所以我們走到了方案三的分支:自己提issue,自己改

解決問題

maintainer溝通

來到了解決問題的步驟,我們就需要先通過提issue的方式,與專案的maintainer明確問題,確定方案。

下面附上我對於這個問題提的issue:https://github.com/mcollina/autocannon/issues/442

image.png

看得出來在我描述完問題,並提出了自己的解決方案之後,這位maintainer非常積極的及時迴應並同意了我的方案。

這也是我想分享的參與開源專案的一個心得:選擇還在頻繁迭代,maintainer積極響應的專案進行參與,不僅提高解決問題的效率,也能增強參與開源的信心

接下來我們就需要深入程式碼進行修改。

修改專案程式碼

由於我們的目的非常明確:支援傳入自定義的驗證函式對responseBody進行驗證

所以我們可以通過從expectBody邏輯入手的方式,在程式碼中增加我們的邏輯。

image.png

expectBody的驗證邏輯在lib/httpClient.jsClient類的constructorparser[HTTPParser.kOnMessageComplete]函式中實現。

可以看到邏輯非常直接,存在這個引數且responseBody與其不是全等,就emit一個mismatch訊息,加入錯誤統計中。同時我們如果想增加自定義校驗邏輯,肯定也是在這個位置。

找到了邏輯實現的位置,我們還面臨接下來的諸多問題:引數定義邏輯在哪裡?引數校驗邏輯在哪裡?worker模式如何傳參?...

帶著這些問題,我們可以以點及面,逐步瞭解專案的全貌。這也是我想分享的閱讀開原始碼的一個心得:帶著問題讀程式碼,在解決問題的過程中,逐步拼上一個個碎片,最後瞭解完整的專案

通過這個思路,我們完成了這個需求的邏輯開發,過程不再贅述。

功能邏輯開發完畢。不過在提PR之前,有幾個問題我們還需要注意。

單元測試

為自己的程式碼增加對應的單測是每個開發者都應該遵守的美德,也是開源社群的公約。完善的單測並通過也是為我們的程式碼增加公信力的最好方式。

程式碼風格

一千個人有一千個哈姆雷特。每個開源專案也都會有自己的程式碼風格,可能與我們平時的程式碼風格不太一致。

這個時候我們應該儘量遵守專案維護者選擇的風格,大部分開源專案也會提供的checklint命令讓維護者自查。(不符合規定風格很有可能不允許提交)

順手優化

在本地除錯的過程中發現,當自定義驗證邏輯執行耗時比較長時(比如responseBody很大,並且需要類似indexOf的邏輯進行驗證),開啟正確性驗證會影響壓測結果

這是一個很明顯的問題,無論如何正確性驗證行為是不應該影響壓測本身的。

通過排查程式碼我們可以得到原因,在之前的實現中發起下一次請求的邏輯阻塞在正確性驗證之後,也就是說只有本次正確性驗證完畢,才會發起下一個請求。所以當正確性驗證邏輯執行時間長時,會導致整體壓測的吞吐量的下降,導致影響壓測資料。

image.png

改造的邏輯其實很簡單:將發起下次請求邏輯調整到正確性驗證之前,也就是onresponse的頂部。也就是說不管後面正確性驗證消耗多少時間,我都會在接到上一次請求的返回的第一時間馬上發起下一次請求。這樣才是真實的壓測結果。

具體程式碼改動如圖:

image.png

提交 Pull Request

前面的工作完畢之後,我們就可以正式的提交PR,等待被Merge到主幹上了。

下面附上我為本次功能提交的PR:https://github.com/mcollina/autocannon/pull/443

image.png

maintainer對程式碼進行review並通過之後,本次PR就成功收尾了,也幫助我成為了autocannoncontributor。筆芯。

總結

最後有幾點經驗想和大家分享一下:

  1. 勇敢的邁出第一步,從實際出發:自己業務中經常使用的開源專案可能更容易發現問題,提出改進。
  2. 選擇合適的開源專案:一個積極響應的maintainer會極大的提高參與開源的積極性與熱情,再次感謝autocannon的作者的鼓勵與幫助。
  3. 帶著問題讀程式碼:對於複雜專案來說,由點及面,由淺入深的閱讀能有效的降低閱讀門檻。

總結一下,本次參與開源是從一個真實的業務場景入手,先從實際使用中發現開源專案存在的優化點,再帶著問題去讀程式碼,由點及面的瞭解整個專案的程式碼結構,從而在原有的基礎上增加自己的功能,成功為開源添磚加瓦。