閒魚單體應用 Serverless 化拆分實踐

作者:閒魚技術——柬超

背景

2018,我們在實踐中提出了 flutter+Dart Faas 的雲端一體化研發解決方案,該方案藉助 Serverless 輕(聚焦業務)、快(單個接口單個函數,研發、部署快)、NoOps(運維平臺化)的能力,降低了服務端業務組裝層的研發門檻,使得客戶端同學也能夠有能力有機會參與到服務端業務開發中,減少了客戶端服務端協作效率問題,提升了新興業務的迭代效率。但是在閒魚傳統應用架構中,也存在着類似業務組裝層,這個應用的名字叫 idleapi。由於應用的垂直業務邊界劃分和架構分層設計不清晰,近乎所有業務都在 idleapi 上迭代。新的業務不斷累加,老的業務不斷迭代,過期的業務又得不到及時的清理,導致應用規模不斷膨脹。據統計,截止 2020 年雙十一,Idleapi 對外提供了 1200 多個網關接口,其中有 500 多個是沒有業務流量的(業務下線),但是代碼依然還在運行,沒有及時清理。 導致 idleapi 總共有 70w + 行代碼,2k + 個業務開關,上百個業務模塊。這麼多業務、代碼、開發都耦合在一個應用上,引發了一些列的隔離性問題:

線上穩定性:

上百個業務模塊運行在一個應用進程中,相互干擾,容易引發隔離性問題。例如一個業務模塊出現問題(將內存耗盡或者將線程池佔滿),就會導致部署在同一臺機器上的其他業務模塊無資源可用,拒絕服務,連累了同機部署的核心業務,就會引發故障。這樣的例子每年都會有。

研發效率低:

幾十個研發同學開發維護上百個業務模塊,每次發佈都會有十幾分支,每增加一個業務分支,都會面臨代碼衝突的風險,分支的基線版本與其他分支的基線版本差距越大,要解決的衝突也就越多,消耗的時間也就越久。據統計,Idleapi 預發發布一次需要 30 分鐘左右,其中有 20 分鐘是在等開發同學解決衝突,開發效率低下。

業務垂直化衝突:

爲了更好的發展業務,關注業務指標,閒魚按照業務域重新組合了人員結構,但是應用結構還來不及跟進。同一業務組內部雖然能夠自治內聚,有效溝通,但是當所有業務耦合在一個應用中時,業務間仍然需要大量精力跨組協同

治理 -- 拆分

The structures of large systems tend to disintegrate during development, qualitatively more so than with small systems. Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations. 根據康威定律:大的系統總是在發展中趨於分解、重組,以達到系統架構和人員結構的某種同態。爲了解決 idleapi 存在的各種問題,我們決定對它進行拆分。拆分過程中,有幾個問題必須要提前考慮清楚:

  1. 拆分的產物是什麼?是按業務域劃分的傳統的單體應用,還是以業務接口爲單位的 FaaS 函數?2. 拆分過程中,業務代碼是全部重寫還是複用?如何處理冗餘的業務代碼?3. 業務的配置、監控、告警如何遷移?4. 如何快速驗證?5. 如何平滑灰度?如何回滾?業務遷移過程中,新的需求如何處理?6. 應用上線後,是否有措施防止再次出現應用 / Faas 膨脹問題? 上述幾個問題是拆分流程的關鍵點,決定着拆分方案能否成功的落地執行。接下來我們逐個分析下:

傳統應用 VS FaaS 函數

拆分要解決的第一個方向性問題: 拆分的目標產物是什麼?思路大致有兩個: 1. 按照業務域拆分成一個一個小的傳統的應用,獨立研發部署運維;2. 以照網關接口爲單位,拆分爲一個個對應的 FaaS 函數。根據這幾年的探索和對比:我們認爲 FaaS 非常適合解決 Idleapi 遇到的問題。

調試期

首先在調試期,傳統應用下,多個接口在一個應用上並行開發,不同分支代碼發佈時存在代碼合併衝突的風險,且預發部署一次大概需要 30 分鐘左右。 而在 Faas 下,一個網關接口,對應一個 Faas 函數,每個 Faas 函數有自己獨立的 git 倉庫和部署環境。Faas 之間相互獨立,物理隔離,開發同學可以放心修改自己的代碼和基線版本,也可以隨時發起遠程 Debug,而不用擔心妨礙其他開發同學調試。而且由於每個 Faas 函數只聚焦於一個業務網關接口,FaaS 函數的代碼量和依賴的二方服務遠小於傳統應用,因此預發部署一次只需要 3 分鐘,比傳統應用快近 10 倍。

運行期

在運行期,每個 Faas 函數運行在不同的集羣上,這種天然的物理隔離性,使得 FaaS 函數不會引發隔離性故障。一個 Faas 函數上面的業務耗盡線程池、寫爆磁盤,都不會影響部署在其集羣上面的函數(業務關聯除外)。

編碼期:

雖然 Faas 函數在調試期,運行期,運維期都佔據優勢,但是傳統的單體應用在編碼期佔據優勢,例如: 代碼複用性:多個業務的代碼在一個工程倉庫裏面,底層的工具類、manager 類,上層業務都可以直接調用,代碼複用簡單直接;而 FaaS 模式下,不同的網關接口分別在不同的代碼倉庫中,代碼複用:需要代碼拷貝或者公共代碼下沉到二方包或者領域服務中,又會引起代碼維護問題。 軟件版本升級:當 Pandora 或者二方包必須要升級時: 傳統應用只需要升級該應用依賴的軟件版本,重新發布就可以解決升級問題。而在 FaaS 模式下:如果每個函數都需要業務開發同學逐個修改和發佈,重複勞動的工作量會是傳統應用的上百倍,十分影響開發效率。我們也在嘗試通過一些平臺化的工具或者分層的措施等方案來解決這個難題。

拆分工具

拆分方案確定後,idleapi 將由一個巨型單體應用,被拆分成幾百個以網關接口爲單位的 FaaS 函數。這麼多的業務重新實現一份是不現實的,所以最佳方式是複用單體應用中的業務代碼。對代碼分析後,我們發現 idleapi 中,各業務的代碼相互引用,形成了一個錯綜複雜的巨型網狀結構。一個業務接口關聯了五個甚至十個其他業務接口的代碼,牽涉的源文件數近 1000,佔 idleapi 代碼源文件總量的 1/4,完全沒有達到我們簡化業務代碼的目的。而且除了業務網關入口外,還存在着其他各種隱式的函數入口,比如:json 序列化會自動調用類的 set 函數等,Bean 的初始化函數等等。對人工拆分業務代碼提出了很大的挑戰。 爲此,我們設計和實現了一個代碼拆分工具,能夠幫助業務在交織如麻的代碼中,分析出業務入口函數所依賴的類、方法和屬性,排除沒有調用到的類、方法和屬性。該工具能夠將單個業務入口所依賴的源文件數量進一步降低到 100 左右,(其中 70% 是接口數據類型)。結合我們設計實現的 Faas 業務框架,業務同學遷移時,能夠一鍵拆分出業務代碼、創建 Faas 函數,並部署到預發環境,整個過程耗時半小時以內。 對於業務開關配置,我們也提供了遷移工具,能夠一鍵將線上或者預發的配置批量遷移到新的函數,免去人工遷移需要逐個審批拷貝的重複勞動。

自動化迴歸測試

測試是保障拆分出的業務代碼質量的最後一道屏障。爲了降低應用拆分給業務和測試同學帶來的額外工作量,我們協同 Faas 平臺和自動化迴歸測試平臺,將錄製回放等迴歸測試功能,適配到 Faas 平臺的 SideCar 和 Pod 架構。開發同學只需要在 FaaS 函數發佈後,在傳統應用中錄製線上流量,然後把流量導入待測 FaaS 函數進行自動化迴歸測試。 通過對接自動化測試平臺,開發同學可以自助完成業務的迴歸測試。降低了業務遷移的風險和測試同學的測試壓力,提升遷移的效率。

運維

在 FaaS 業務的運維方面,我們儘量保留開發同學的運維習慣:拆分出的 FaaS 函數保留了單體應用中日誌的名稱、日誌的組織格式、編碼等等,也保留了開發同學登錄遠程機器的能力。同時,我們將業務個性化日誌適配到 Faas 平臺的白屏化日誌功能,開發同學可以通過管控平臺查看搜索任意機器上的所有日誌,相比登陸機器逐個查看,提效很多。同時,基於日誌的監控告警系統只需要更新下相關監控的業務日誌路徑就能夠完成監控的遷移。

架構演進

對於應用拆分爲細粒度 Faas 函數後,業務代碼複用問題,解決方案大致有兩種思路: 一.先治理再拆分:先對單體應用進行改造重構,把各業務複用的代碼下沉(下沉到公共二方包或下沉到該業務領域服務層),然後再吧單體應用拆分爲多個 Faas 函數。這個方案存在 2 個問題:1. 殭屍代碼佔比僅一半,會帶來無效的重構工作量,2. 原有應用上進行重構,新的業務迭代和重構 AB 混雜在一起做開發、做灰度,複雜度高,風險大。 二.先拆分後治理:先對單體應用進行業務拆分,暫時忽略代碼複用的問題,等函數拆分之後,有業務同學在後續的開發過程中,根據實際業務需要進行代碼複用改造。將業務複用代碼或獨立爲工作二方包,或下沉到領域服務中。相比第一種方案,在隔離清晰的函數代碼庫之間梳理複用性問題,複雜度和風險會小很多。因此,我們選擇了第二種方案。

收益

目前已經有 30 + 個網關接口從單體應用中拆分出來,並交付業務開發維護,進一步驗證了該方案在單體應用的拆分治理方面是可行的。後續我們會將拆分方案提供給開發同學,由開發同學自行拆分遷移業務。拆分後,業務保留了原有的開發運維習慣。 同時,一個業務網關接口對應一個函數的規則,使得一個 Faas 函數只聚焦於一個業務網關接口,解決了業務不斷推陳出新的場景下傳統應用不斷膨脹的難題。這種聚焦性,也使得函數代碼量只有傳統應用的 3% 不到(且以數據類居多),業務發佈一次僅需要 5 分鐘(Java)

總結

總體來看,藉助自動化拆分工具,業務同學能夠在半小時內一鍵拆分出一個業務接口,並預發部署,中間過程不需要人工干預,且拆分出的函數保持了原有開發運維習慣,遷移成本低,能夠被業務同學接受。而且藉助函數的業務聚焦性,一個接口一個函數,各函數在開發期沒有其他業務的干擾,可測性高,部署速度快。在運行期,各函數運行在不同的物理機,這種天然的物理隔離,大幅提升了運行期的穩定性,降低了業務的運維成本。

展望

目前 Faas 函數平臺還在快速發展中,還存在着一些待改進的地方: 機器成本 小流量函數機器成本高:在集團安全生產的高要求下,即使是小流量函數,也需要每個機房兩臺機器,浪費嚴重,平臺正在考慮通過降低機器規格和超賣等多種措施提升機器利用率。 彈性:在業務上下游鏈路比較長的情況下,單點的彈性並不能解決所有問題,這需要通盤考慮和解決。 維護成本 統一升級:在集團卡口發佈時,每個函數都需要修復問題重新發布,這是一個巨大的工作量,相關解決方案我們正在探索實踐中。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://juejin.cn/post/6960864964227825695