嘗試爲nodejs貢獻代碼

1 需求背景

一直都有在看一些開源項目的代碼,但是還沒有試過提交pr。因爲最近在研究websocket和keep-alive。而websocket涉及到長連接,過多無用的長連接對系統來說是負擔,是否可以儘快發現對端是否已經掉線,從而釋放這個連接來減少系統壓力呢,就這個初衷,想通過wireshark和nodejs調試一下心跳機制,但是發現nodejs對這個的支持不是很好。tcp的心跳機制,支持三個配置,但是nodejs的setKeepAlive只支持一個配置(後面發現最新版代碼裏有一點支持的痕跡了,但是沒有給用戶提供接口),所以就產生了提交pr的想法。代碼改動不大,但是整個流程走下來,也挺費時間的。
    本文大致分享一下這個過程。我的訴求是想讓nodejs把修改心跳機制和相關配置的接口暴露給用戶。但是libuv層的接口本身就不支持這個能力。所以要解決這個問題,要修改c、c++、js的代碼。因爲nodejs的架構就是這樣,libuv提供能力,c++套殼,js調用。所以你想加一個libuv不支持的功能時,你就得從libuv改起。

2 技術背景

tcp連接一旦建立,默認是不會斷開的,但是操作系統支持心跳機制,只不過默認是關閉的,心跳機制有幾個配置,分別是是否開啓,多久沒有收到數據或ack後開啓發送第一個心跳包,隔多久發送一個,發送多少個後認爲連接斷開。但是有一個問題是,心跳機制並不是什麼時候都好使,這大概源於tcp協議的複雜性。如果兩端都沒有數據來往時,心跳機制能很好地工作,但是一旦本端有數據發送的時候,他就會抑制心跳機制。我們看一下linux內核5.7.7的一段相關代碼。

上面這一段是心跳機制中,定時器超時時,執行的一段邏輯,我們只需要關注紅色框裏的代碼。一般來說,心跳定時器超時,操作系統會發送一個新的心跳包,但是如果發送隊列裏還有數據沒有發送,那麼操作系統會優先發送。或者發送出去的沒有ack,也會優先觸發重傳。這時候心跳機制就失效了。所以這裏除了想讓nodejs支持keep-alive的配置外。還加入了linux的另一個屬性TCP_USER_TIMEOUT。這個屬性的功能是,在多久沒有收到ack後,操作系統就認爲這個連接斷開了。看一下相關代碼。
設置閾值
這是設置閾值的代碼。

這是超時時判斷是否斷開連接的代碼。我們看到有兩個情況下操作系統會認爲連接斷開了。
1 設置了TCP_USER_TIMEOUT時,如果發送包數量大於1並且當前時間具體上次收到包的時間間隔已經達到閾值。
2 沒有設置TCP_USER_TIMEOUT,但是心跳包發送數量達到閾值。
所以我們可以同時設置這兩個屬性。保證心跳機制可以正常運行。

3 開始寫代碼

有了訴求,那就開啓寫代碼。首先到nodejs倉庫fork一份代碼出來,然後按照nodejs官方給的流程,最後提交pr。第一次提交pr的時候,reviewer建議我使用新接口的方式修改這個代碼,因爲我是修改setKeepAlive相關的代碼,然後做了兼容處理。後面發現reviewer給的建議比較好,修改存量接口存在風險,兼容處理非常麻煩,提供新接口然用戶切換會比較安全並且代碼上也比較好寫。整個過程比較麻煩的是,每次寫完需要編譯(尤其第一次),然後跑測試用例。這很費時間。還有一些代碼風格的問題。另外libuv的修改是在libuv倉庫,不是在nodejs倉庫。

4 結果

下面是跑測試用例的漫長過程。
跑測試用例
下面是修改的代碼文件

下面是pr,還沒review。
nodejs: https://github.com/nodejs/node/pull/34193
libuv: https://github.com/libuv/libuv/pull/2907

總結:第一次嘗試參與開源,感覺很有趣,但是也比較累,整個流程走下來也挺費時間,不過爲世界級軟件貢獻代碼大概也是每個技術人的一個目標,不管怎樣,也是一個很好的嘗試。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章