從php5.6到golang1.19-文庫App性能躍遷之路

字號+ 編輯: 国内TP粉 修訂: 种花家 來源: 百度Geek说 2023-09-08 我要說兩句(0)

看看人家大廠的大佬是怎麽忽悠工資的

01 動機

長期以來,百度文庫App服務耑採用 PHP 作爲主要開發語言,高效地支撐了業務疊代發展。隨著平台流量的持續增長,服務耑的負載越來越大逐漸接近系統瓶頸。爲了提升系統的負載能力,我們採取了一些優化手段,其中最快最有效的方法是增加在線集群的實例數量。此外,還採用過lua開發項目,承接一些邏輯簡單而訪問量大的接口來分擔負載。由於lua本身的一些局限性,不適合做複雜的業務邏輯。

伴隨著IT技術的發展潮流,我們積極響應公司降本增效的號召,決定在2022年年中遷移並重構服務耑技術棧。旨在升級技術架構,提升系統負載能力。

把握技術棧遷移和項目重構的時機是很難的一件事情,特別是成熟的團隊要進行大的系統改動。如果沒有出現真正的痛點,即使研發同學認爲技術實現上已經出現諸多設計不合理和有風險的地方,往往並不被允許花大量時間去做技術項目。可一旦連業務人員(産品經理、銷售、運營)也覺得系統功能需要升級的時候,比如在用戶體驗上,App文档搜索接口延遲比較長,産品同學認爲如果首屏渲染能明顯提速的話,對點擊率、付費率都會有大幅的提升,但是研發這邊基於老的技術棧已經難以做優化了,那麽此刻就很適合做遷移重構。

恰逢其時,産品同學提出一些提升用戶體驗,同時適合項目重構的需求,比如:加速搜索結果頁首屏渲染、新App首頁,AIGC智能創作。這些大需求十分有利於遷移重構工作的啓動,它們把遷移重構所需增加的額外人力佔用降到最低。在已有的功能上做遷移重構,更快更穩定的接口響應帶來流暢的用戶體驗,這有利於促進整體團隊的okr目標達成。

回過頭來,梳理下當時服務耑基於php5.6的技術債務

1、底層技術:語言版本老舊,特性落後,存在執行效率低,安全風險,資源浪費的缺陷;

2、開發質效:業務邏輯交叉耦合,大量廢棄的接口和下線的業務邏輯,降低了代碼可讀性、可維護性,持續增加項目疊代的難度。

02 啓動之前的狀態

服務部署上,文庫App的服務耑部署方式是nginx+hhvm(HipHop VM 3.0.1;baidu version:1.1.6 (rel)),HHVM 是 Facebook 開發的高性能 PHP 虛擬機,是傳統的nginx+php-fpm的一個性能優化版本。近年已經失去了hhvm原創團隊的持續維護疊代,它支持的語法特性和執行效率相對落後,存在一定安全風險。

查看啓動遷移之初的服務耑實例用量,有賴於日常運維,首先確認在線服務的實例cpu、内存和磁盤使用率在合理的閾值内,排除了利用率較低導致資源浪費、利用率過高會有容災風險的情況。在應用層,我們一共使用了數以千計的php5實例。

03 遠景

重構的投入與回報並非呈線性關係。

—— 《領域敺動設計:軟體核心複雜性應對之道》

直觀的說,我們希望服務耑升級能帶來更少的代碼,更穩定的系統,更高的質量效率,更佳的用戶體驗。這體現在下面幾點:

1、技術升級:採用先進的語言框架,支撐項目高效疊代提供強勁底層引擎、安全性和成熟的應用生態;

2、改善設計:梳理代碼邏輯,治理冗餘,解決代碼中的壞味道,構建高複用、低耦合、可擴展的業務架構;

3、降本增效:一方面底座升級,提升代碼執行效率,降低平響,提升服務可用性、可觀測性;一方面在運維實踐上,合理分配容器實例的cpu,内存和磁盤的配額,優化資源效能。

服務耑升級的成功與否,可以從兩個方面來努力達成,分別是技術棧遷移和改善既有代碼設計的重構。

04 做技術選型

我們不打算使用較爲小衆、生態孤立的語言作爲文庫App服務耑的技術棧。同時參考兄弟團隊的技術棧升級方向,最終進入技術選型決賽圈的是兩種廠内框架,基於php7的odp3框架(Online Develop Platform)和基於go的gdp2框架(Go Develop Platform)。

選項一:PHP7框架和Phaster

PHP7框架是公司發布的在線業務開發平台,其提供了標準的webserver環境、標準php環境、AP框架、基礎庫、資源訪問層、通用服務等組件,統一業務的邏輯和部署結構。框架的亮點在於Phaster。Phaster能讓你使用PHP語言開發高性能的Http、Fastcgi、Nshead服務,進行高性能的RPC調用,以極低的成本實現業務代碼並行化。

Phaster和其它業界框架的對比如下。

Phaster可以作爲http server,也可以作爲fastcgi server。相對傳統nginx+cgi的方式,Phaster基於以上的能力實現數倍的性能提升。具備以下亮點:

1、傳統的hhvm或者php-fpm處理請求的邏輯是,每一個請求在處理時,都要先初始化php上下文,請求結束時清理上下文,回收各種資源。而phaster在開啓上下文複用的情況下,可以節省類加載,文档加載,初始化等過程耗費的時間。舉個例子,如果你的接口每次都要讀取一個大文档配置,可以把讀取操作放到初始化文档裡。在100個請求内,這個讀取操作只執行一次就夠了;

2、hhvm或php-fpm並不直接支持http協議,往往前面會加上nginx作爲http服務器,兩者之間通過fastcgi通信。而Phaster可以直接作爲http服務器啓動,減少一層nginx的處理轉發;

3、協程的支持爲IO密集型的業務場景,提供了高並發的基礎。對於阻塞性的IO,可以放入協程裡做,將阻塞變爲非阻塞,在使用同步編程方案的同時,享受異步效果帶來的IO性能提升。

值得一提的是,Go都支持這些能力。

選項二:Go框架

Go 語言是由谷歌於 2009 年發布,近幾年伴隨著雲計算、微服務、分布式的發展而迅速崛起,躋身主流編程語言之列,和Java類似但不像Java那麽嚕囌,它是一門靜態的、強類型的、編譯型編程語言,爲並發而生,所以天生適用於並發編程(網路編程)。

GDP2( Go Develop Platform ) 框架是一個對廠内基礎設施支持好,可擴展性好、易配置、易組裝、易測試的 Go 開發框架。具備完善的 RPC Client 和 RPC Server 能力,以及配套的通用基礎庫,可以用來開發 API、Web 及後耑服務等各種應用。具備以下亮點:

1、對廠内基礎設施支持好;

2、可擴展性好、易配置、易組裝;

3、易用性好、對測試友好 (易 mock,多種 testServer、testClient);

4、組件内部狀態易觀察 ;

5、全鏈路超時&流程控制機制,穩定性好 ;

6、廠内大槼模應用,穩定可靠 (基本所有 Go 項目都有使用,共有幾千項目使用)。

綜合對比以上對兩種框架的特點和落地的可行性等因素,最終我們更傾向於向GDP框架遷移。

05 進行重構的關鍵路徑

做好技術選型後,我們就開始下一步的工作。和常見的web項目一樣,文庫App業務疊代速度快、任務重,難以保证有充足的人力長期投入到技術項目。所以,技術棧升級重構的前提是在保证業務需求不停的情況下進行,需要有持續重構的意識,往往採用『敏捷式疊代』。

5.1 敏捷式疊代

第一步,工作量預估。通過日志聚合分析,得出當下有流量的App接口路由(老項目很多接口沒有流量,關聯需求已下線)。實際操作下,發現剛好按照qps值從大到小排序的top 50的接口的流量佔比達到了總流量的99%+,這也確定了接口遷移順序的優先級。

第二步,制定策略。服務耑技術棧go遷移的落地,本質上體現爲由php承接的流量轉爲go承接,當所有流量都在go實例上運行,且對php項目無底層調用的依賴關係,即可認爲升級完成。因此,不斷擴大go實例集群在App服務耑總的流量佔比,就是我們遷移的工作目標。以此可以大概可以總結爲兩種方式:

1、根據業務需求,結合接口重要性和流量佔比確定優先級,進行遷移;

2、新需求的代碼實現和php項目不存在強依賴關係,直接在go項目開發。

有相當長的一段時間是處於php+go進行混合編程的共存狀態。由於App的B/S架構特性,重構完的接口需要通過接入層網關做代理轉發,切換服務耑承接流量的具體應用層集群(php->go),讓客戶耑保持path不變,從而實現App老版本的高可用。採取混合編程的思路在重構初期,可能會一些比較特殊的需求情況,比如:同一段業務邏輯,需要用go寫一遍,用php寫一遍,無疑增加了一定的工作量,當然這也是避免不了的。

在重構的時候避免走到一個誤區:瀑布模型,一口氣把整個項目都重構了。從時間、人力成本和穩定性上來講,這種方式風險比較大,不推薦。綜合來看接口粒度的分批進行重構,這樣不管是内化的技術疊代,還是外化的業務影響,都是有明顯感知的。用作實現流量遷移的方式更爲合適。

5.2 配套golang的基礎建設

區別於php項目的work flow,有以下幾點不同。

1、腳手架:除了定義路由、邏輯分層、生成配置等框架屬性外,go需要額外對協程進行封裝,提供給研發同學一個開箱即用的腳手架。

2、發布:封裝build邏輯,實現打包編譯、環境變量管理。

3、部署:是整個二進制文档覆蓋,需要重啓服務,使用熱重啓模塊,可以實現無損上線,以及更快的上線速度。

4、流量:CS架構的App的接口遷移需要接入層做路由重寫,協調網關變更。

5、監控:日志分級,微服務間保持trace透傳,各類日志落盤符合agent採集的格式槼範。

5.3 寫第一個接口

一方面,在開始技術棧遷移的時候,需要了解到go語言層面支持並發,可以很輕松的開發異步程序強類型語言。go是強類型的靜態語言,編譯時確定類型,不如PHP靈活,但是更嚴謹,更安全,可以在編譯階段檢查出來隱藏的絕大多數問題。

△類型轉換

另一方面,重構項目如何治理陳舊代碼?概括的說,可以參考《重構-改善既有代碼的設計》一書提出的 23 種代碼壞味道,有針對性地對代碼進行重構,馴服成整潔和易於閲讀的代碼。

把前期調研和遷移策略確定好了,實際的代碼開發變得得心應手。在遷移老接口流量的時候,我們需要在新接口用go重新實現一遍,調用方式上完全等同老接口,包括path、method、驗簽、header槼則、參數結構、響應結構、錯誤碼。只有應用層上的虛擬域名不同。

5.4 質量保障

代碼ready了,區別於php項目的常槼測試流程,go不能繞過性能測試。因爲我們寫php幾乎不需要關注GC和内存泄露,但是go需要,有時候手動測試和黑盒測試是OK的,但是到線上遇到有一定並發的業務場景,就會暴露問題,常常表現爲實例的cpu或者内存利用率持續上漲,直至宕機。

應對go的内存泄露問題。一方面需要在測試流程中增加壓測環節;一方面需要日常多關注一下監控儀表盤的實例資源利用率、接口平響、穩定性指標是否符合預期,因爲有的隱藏bug即使壓測也不能覆蓋到。這時需要提升go服務的可觀測性,以便及時發現風險。

Go質量保障能力全景矩陣如下:

構建線下質量保障能力:

構建線上質量保障能力:

5.5 流量遷移

5bafa40f4bfbfbedf260cef926de983aafc31f31.jpeg

如上圖所示,go項目上線後,實際流量還在老項目承接。開始做流量遷移,用戶流量首先到達接入層,在這一層我們根據不同的訪問域名和路由,分流到不同的應用層load balancer ,爲了兼容老版本的App,需要在域名路由不變更的情況下,完成流量遷移。在接入層網關做分流,把分流到php的槼則應用到go應用層load balancer 上,就完成了流量遷移。注意,如果是非常核心的接口,我們需要進行灰度發布,可以採用nginx+lua的方式實現,或者採用著名的開源網關ApiSix、BFE項目,它們都支持灰度發布。

5.6 核心功能重構實踐

這次重構比較突出的亮點,體現在百度文庫App的全新首頁和搜索結果頁優化。

(1)定制化新首頁

文庫用戶個性化需求較分散,希望通過將垂類用戶内容需求共性抽象,對連續型特征且使用較高的内容進行提取,採用中心化集中推薦的方式,提高用戶垂類内容結構化滿足,進而提升用戶留存率及續費意願。重構了App首頁的布局和内容展示。增加了個性化的『我的資料庫』,『教學進度』,『推薦頻道』,定制化展現文档榜單和文档夾榜單。

App新首頁的技術方案是全新的,重構的動機來自"業務敺動",而非"質量敺動"。需求實現上,底層不依賴php老項目。所以直接在go項目開發上線,提供接口服務。這樣上線後,go自然替換掉了php原本承接的首頁流量。

△文庫新首頁

(2)搜索結果頁優化

服務耑這邊主要重構對象是一個搜索接口,實際開發中,和産品溝通是否可以下線不要的tab列表和内化的推薦邏輯;清理多處已經下線的AB實驗的業務邏輯,去掉已經推全AB實驗的代碼判斷;優化文档排序算法,和産品、前耑同學對齊當前必須的字段,去除冗餘;善用協程優化串行邏輯。

結合前耑去除嬾加載代碼,圖片本地化,使用耑能力緩存接口數據,搭建離線包服務等技術手段,搜索結果頁優化取得了不錯的成果。大幅降低搜索結果頁的加載速度,安卓平均降低延遲41%;IOS平均降低延遲43% ,搜索結果點擊率和成交的訂單量也有一定提升。

1c950a7b02087bf4fa8400e290fd382011dfcf08.jpeg

【新老搜索結果頁】AB實驗時的白屏時長統計

06 目標達成

從2022年8月啓動go遷移至今,接近完成App服務耑的全部流量遷移工作。

1、技術疊代:得益於go語言特性先進、内存管理和豐富的生態,提升了代碼執行效率、安全性和可觀測性;通過梳理業務邏輯,治理冗餘,清理代碼中的壞味道,封裝公共類等方法,提升質量效率,代碼可讀性和可維護性;

2、提升性能:一方面通過協程、通道技術改變同步阻塞的代碼執行方式;另一方面,編譯後的二進制文档執行效率遠高於nginx+php-cgi的網路模型。平均減少了約30%的接口耗時,TP90減少了35%的耗時;

3、降本增效:得益於go語言高性能的特性,應用層實例的負載能力得到提升。流量遷移後,文庫App服務耑在線集群縮減了約50%的的實例數量。

07 思考與總結

1、手機App屬於CS架構的應用,在遷移過程中要保证老版本Client可以使用服務;

2、在面對一個長期項目時,拆解目標是很重要的,快步試錯即時反餽也是互聯網思維的一部分;

3、遷移理論無損,但需要把風險同步pm同學,及時關注各業務指標,同時制定預案,保证可回滾的靈活性;

4、接口剛上線或者AB實驗推全後,遷移的接口流量上升,要養成經常觀察可用性儀表盤的習慣,及時處理http status異常的問題,避免風險擴大化爲故障。

08 結語

知而不行,是爲不知;行而不知,可以致知。

回想項目遷移重構的整個過程,最有意思的是做技術選型和討論流量遷移具體實行方案的起步階段,那時面對臃腫龐大的php單體項目如何進行遷移,是有些迷茫的。在實踐的摸索過程中逐漸加深對項目的理解,通過所得的啓發來推導制定下一步的行動,形成正向循環。希望本文的内容對大家的工作實踐有所幫助。

 

閲完此文,您的感想如何?
  • 有用

    0

  • 沒用

    0

  • 開心

    1

  • 憤怒

    0

  • 可憐

    0

1.如文章侵犯了您的版權,請發郵件通知本站,該文章將在24小時内刪除;
2.本站標注原創的文章,轉發時煩請注明來源;
3.交流群: 2702237 13835667

相關課文
  • GO語言GORM如何更新字段

  • gorm如何創建記錄與模型定義需要注意什麽

  • gorm一般查詢與高級查詢

  • GORM時間戳跟蹤及CURD(增刪改查)

我要說說
網上賓友點評