作者 | 百度文庫App
導(dǎo)讀
本文深入淺出地分享了百度文庫App服務(wù)端技術(shù)棧從PHP遷移至Go的實(shí)戰(zhàn)經(jīng)驗(yàn),包含了技術(shù)選型、基礎(chǔ)建設(shè)、流量遷移的具體方案,以及核心項(xiàng)目案例的重構(gòu)實(shí)踐。
全文6209字,預(yù)計(jì)閱讀時(shí)間16分鐘。
01 動(dòng)機(jī)
長期以來,百度文庫App服務(wù)端采用 PHP 作為主要開發(fā)語言,高效地支撐了業(yè)務(wù)迭代發(fā)展。隨著平臺(tái)流量的持續(xù)增長,服務(wù)端的負(fù)載越來越大逐漸接近系統(tǒng)瓶頸。為了提升系統(tǒng)的負(fù)載能力,我們采取了一些優(yōu)化手段,其中最快最有效的方法是增加在線集群的實(shí)例數(shù)量。此外,還采用過lua開發(fā)項(xiàng)目,承接一些邏輯簡單而訪問量大的接口來分擔(dān)負(fù)載。由于lua本身的一些局限性,不適合做復(fù)雜的業(yè)務(wù)邏輯。
伴隨著IT技術(shù)的發(fā)展潮流,我們積極響應(yīng)公司降本增效的號(hào)召,決定在2022年年中遷移并重構(gòu)服務(wù)端技術(shù)棧。旨在升級(jí)技術(shù)架構(gòu),提升系統(tǒng)負(fù)載能力。
把握技術(shù)棧遷移和項(xiàng)目重構(gòu)的時(shí)機(jī)是很難的一件事情,特別是成熟的團(tuán)隊(duì)要進(jìn)行大的系統(tǒng)改動(dòng)。如果沒有出現(xiàn)真正的痛點(diǎn),即使研發(fā)同學(xué)認(rèn)為技術(shù)實(shí)現(xiàn)上已經(jīng)出現(xiàn)諸多設(shè)計(jì)不合理和有風(fēng)險(xiǎn)的地方,往往并不被允許花大量時(shí)間去做技術(shù)項(xiàng)目??梢坏┻B業(yè)務(wù)人員(產(chǎn)品經(jīng)理、銷售、運(yùn)營)也覺得系統(tǒng)功能需要升級(jí)的時(shí)候,比如在用戶體驗(yàn)上,App文檔搜索接口延遲比較長,產(chǎn)品同學(xué)認(rèn)為如果首屏渲染能明顯提速的話,對(duì)點(diǎn)擊率、付費(fèi)率都會(huì)有大幅的提升,但是研發(fā)這邊基于老的技術(shù)棧已經(jīng)難以做優(yōu)化了,那么此刻就很適合做遷移重構(gòu)。
恰逢其時(shí),產(chǎn)品同學(xué)提出一些提升用戶體驗(yàn),同時(shí)適合項(xiàng)目重構(gòu)的需求,比如:加速搜索結(jié)果頁首屏渲染、新App首頁,AIGC智能創(chuàng)作。這些大需求十分有利于遷移重構(gòu)工作的啟動(dòng),它們把遷移重構(gòu)所需增加的額外人力占用降到最低。在已有的功能上做遷移重構(gòu),更快更穩(wěn)定的接口響應(yīng)帶來流暢的用戶體驗(yàn),這有利于促進(jìn)整體團(tuán)隊(duì)的okr目標(biāo)達(dá)成。
回過頭來,梳理下當(dāng)時(shí)服務(wù)端基于php5.6的技術(shù)債務(wù):
1、底層技術(shù):語言版本老舊,特性落后,存在執(zhí)行效率低,安全風(fēng)險(xiǎn),資源浪費(fèi)的缺陷;
2、開發(fā)質(zhì)效:業(yè)務(wù)邏輯交叉耦合,大量廢棄的接口和下線的業(yè)務(wù)邏輯,降低了代碼可讀性、可維護(hù)性,持續(xù)增加項(xiàng)目迭代的難度。
△技術(shù)債務(wù)
02 啟動(dòng)之前的狀態(tài)
服務(wù)部署上,文庫App的服務(wù)端部署方式是nginx+hhvm(HipHop VM 3.0.1;baidu version:1.1.6 (rel)),HHVM 是 Facebook 開發(fā)的高性能 PHP 虛擬機(jī),是傳統(tǒng)的nginx+php-fpm的一個(gè)性能優(yōu)化版本。近年已經(jīng)失去了hhvm原創(chuàng)團(tuán)隊(duì)的持續(xù)維護(hù)迭代,它支持的語法特性和執(zhí)行效率相對(duì)落后,存在一定安全風(fēng)險(xiǎn)。
查看啟動(dòng)遷移之初的服務(wù)端實(shí)例用量,有賴于日常運(yùn)維,首先確認(rèn)在線服務(wù)的實(shí)例cpu、內(nèi)存和磁盤使用率在合理的閾值內(nèi),排除了利用率較低導(dǎo)致資源浪費(fèi)、利用率過高會(huì)有容災(zāi)風(fēng)險(xiǎn)的情況。在應(yīng)用層,我們一共使用了數(shù)以千計(jì)的php5實(shí)例。
03 遠(yuǎn)景
重構(gòu)的投入與回報(bào)并非呈線性關(guān)系。
—— 《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì):軟件核心復(fù)雜性應(yīng)對(duì)之道》
直觀的說,我們希望服務(wù)端升級(jí)能帶來更少的代碼,更穩(wěn)定的系統(tǒng),更高的質(zhì)量效率,更佳的用戶體驗(yàn)。這體現(xiàn)在下面幾點(diǎn):
1、技術(shù)升級(jí):采用先進(jìn)的語言框架,支撐項(xiàng)目高效迭代提供強(qiáng)勁底層引擎、安全性和成熟的應(yīng)用生態(tài);
2、改善設(shè)計(jì):梳理代碼邏輯,治理冗余,解決代碼中的壞味道,構(gòu)建高復(fù)用、低耦合、可擴(kuò)展的業(yè)務(wù)架構(gòu);
3、降本增效:一方面底座升級(jí),提升代碼執(zhí)行效率,降低平響,提升服務(wù)可用性、可觀測性;一方面在運(yùn)維實(shí)踐上,合理分配容器實(shí)例的cpu,內(nèi)存和磁盤的配額,優(yōu)化資源效能。
服務(wù)端升級(jí)的成功與否,可以從兩個(gè)方面來努力達(dá)成,分別是技術(shù)棧遷移和改善既有代碼設(shè)計(jì)的重構(gòu)。
04 做技術(shù)選型
我們不打算使用較為小眾、生態(tài)孤立的語言作為文庫App服務(wù)端的技術(shù)棧。同時(shí)參考兄弟團(tuán)隊(duì)的技術(shù)棧升級(jí)方向,最終進(jìn)入技術(shù)選型決賽圈的是兩種廠內(nèi)框架,基于php7的odp3框架(Online Develop Platform)和基于go的gdp2框架(Go Develop Platform)。
選項(xiàng)一:PHP7框架和Phaster
PHP7框架是公司發(fā)布的在線業(yè)務(wù)開發(fā)平臺(tái),其提供了標(biāo)準(zhǔn)的webserver環(huán)境、標(biāo)準(zhǔn)php環(huán)境、AP框架、基礎(chǔ)庫、資源訪問層、通用服務(wù)等組件,統(tǒng)一業(yè)務(wù)的邏輯和部署結(jié)構(gòu)??蚣艿牧咙c(diǎn)在于Phaster。Phaster能讓你使用PHP語言開發(fā)高性能的Http、Fastcgi、Nshead服務(wù),進(jìn)行高性能的RPC調(diào)用,以極低的成本實(shí)現(xiàn)業(yè)務(wù)代碼并行化。
Phaster和其它業(yè)界框架的對(duì)比如下。
Phaster可以作為http server,也可以作為fastcgi server。相對(duì)傳統(tǒng)nginx+cgi的方式,Phaster基于以上的能力實(shí)現(xiàn)數(shù)倍的性能提升。具備以下亮點(diǎn):
1、傳統(tǒng)的hhvm或者php-fpm處理請(qǐng)求的邏輯是,每一個(gè)請(qǐng)求在處理時(shí),都要先初始化php上下文,請(qǐng)求結(jié)束時(shí)清理上下文,回收各種資源。而phaster在開啟上下文復(fù)用的情況下,可以節(jié)省類加載,文件加載,初始化等過程耗費(fèi)的時(shí)間。舉個(gè)例子,如果你的接口每次都要讀取一個(gè)大文件配置,可以把讀取操作放到初始化文件里。在100個(gè)請(qǐng)求內(nèi),這個(gè)讀取操作只執(zhí)行一次就夠了;
2、hhvm或php-fpm并不直接支持http協(xié)議,往往前面會(huì)加上nginx作為http服務(wù)器,兩者之間通過fastcgi通信。而Phaster可以直接作為http服務(wù)器啟動(dòng),減少一層nginx的處理轉(zhuǎn)發(fā);
3、協(xié)程的支持為IO密集型的業(yè)務(wù)場景,提供了高并發(fā)的基礎(chǔ)。對(duì)于阻塞性的IO,可以放入?yún)f(xié)程里做,將阻塞變?yōu)榉亲枞?,在使用同步編程方案的同時(shí),享受異步效果帶來的IO性能提升。
值得一提的是,Go都支持這些能力。
選項(xiàng)二:Go框架
Go 語言是由 Google 于 2009 年發(fā)布,近幾年伴隨著云計(jì)算、微服務(wù)、分布式的發(fā)展而迅速崛起,躋身主流編程語言之列,和 Java 類似,它是一門靜態(tài)的、強(qiáng)類型的、編譯型編程語言,為并發(fā)而生,所以天生適用于并發(fā)編程(網(wǎng)絡(luò)編程)。
GDP2( Go Develop Platform ) 框架是一個(gè)對(duì)廠內(nèi)基礎(chǔ)設(shè)施支持好,可擴(kuò)展性好、易配置、易組裝、易測試的 Go 開發(fā)框架。具備完善的 RPC Client 和 RPC Server 能力,以及配套的通用基礎(chǔ)庫,可以用來開發(fā) API、Web 及后端服務(wù)等各種應(yīng)用。具備以下亮點(diǎn):
1、對(duì)廠內(nèi)基礎(chǔ)設(shè)施支持好;
2、可擴(kuò)展性好、易配置、易組裝;
3、易用性好、對(duì)測試友好 (易 mock,多種 testServer、testClient);
4、組件內(nèi)部狀態(tài)易觀察 ;
5、全鏈路超時(shí)&流程控制機(jī)制,穩(wěn)定性好 ;
6、廠內(nèi)大規(guī)模應(yīng)用,穩(wěn)定可靠 (基本所有 Go 項(xiàng)目都有使用,共有幾千項(xiàng)目使用)。
綜合對(duì)比以上對(duì)兩種框架的特點(diǎn)和落地的可行性等因素,最終我們更傾向于向GDP框架遷移。
05 進(jìn)行重構(gòu)的關(guān)鍵路徑
做好技術(shù)選型后,我們就開始下一步的工作。和常見的web項(xiàng)目一樣,文庫App業(yè)務(wù)迭代速度快、任務(wù)重,難以保證有充足的人力長期投入到技術(shù)項(xiàng)目。所以,技術(shù)棧升級(jí)重構(gòu)的前提是在保證業(yè)務(wù)需求不停的情況下進(jìn)行,需要有持續(xù)重構(gòu)的意識(shí),往往采用『敏捷式迭代』。
5.1 敏捷式迭代
第一步,工作量預(yù)估。通過日志聚合分析,得出當(dāng)下有流量的App接口路由(老項(xiàng)目很多接口沒有流量,關(guān)聯(lián)需求已下線)。實(shí)際操作下,發(fā)現(xiàn)剛好按照qps值從大到小排序的top 50的接口的流量占比達(dá)到了總流量的99%+,這也確定了接口遷移順序的優(yōu)先級(jí)。
第二步,制定策略。服務(wù)端技術(shù)棧go遷移的落地,本質(zhì)上體現(xiàn)為由php承接的流量轉(zhuǎn)為go承接,當(dāng)所有流量都在go實(shí)例上運(yùn)行,且對(duì)php項(xiàng)目無底層調(diào)用的依賴關(guān)系,即可認(rèn)為升級(jí)完成。因此,不斷擴(kuò)大go實(shí)例集群在App服務(wù)端總的流量占比,就是我們遷移的工作目標(biāo)。以此可以大概可以總結(jié)為兩種方式:
1、根據(jù)業(yè)務(wù)需求,結(jié)合接口重要性和流量占比確定優(yōu)先級(jí),進(jìn)行遷移;
2、新需求的代碼實(shí)現(xiàn)和php項(xiàng)目不存在強(qiáng)依賴關(guān)系,直接在go項(xiàng)目開發(fā)。
有相當(dāng)長的一段時(shí)間是處于php+go進(jìn)行混合編程的共存狀態(tài)。由于App的B/S架構(gòu)特性,重構(gòu)完的接口需要通過接入層網(wǎng)關(guān)做代理轉(zhuǎn)發(fā),切換服務(wù)端承接流量的具體應(yīng)用層集群(php->go),讓客戶端保持path不變,從而實(shí)現(xiàn)App老版本的高可用。采取混合編程的思路在重構(gòu)初期,可能會(huì)一些比較特殊的需求情況,比如:同一段業(yè)務(wù)邏輯,需要用go寫一遍,用php寫一遍,無疑增加了一定的工作量,當(dāng)然這也是避免不了的。
在重構(gòu)的時(shí)候避免走到一個(gè)誤區(qū):瀑布模型,一口氣把整個(gè)項(xiàng)目都重構(gòu)了。從時(shí)間、人力成本和穩(wěn)定性上來講,這種方式風(fēng)險(xiǎn)比較大,不推薦。綜合來看接口粒度的分批進(jìn)行重構(gòu),這樣不管是內(nèi)化的技術(shù)迭代,還是外化的業(yè)務(wù)影響,都是有明顯感知的。用作實(shí)現(xiàn)流量遷移的方式更為合適。
5.2 配套golang的基礎(chǔ)建設(shè)
區(qū)別于php項(xiàng)目的work flow,有以下幾點(diǎn)不同。
1、腳手架:除了定義路由、邏輯分層、生成配置等框架屬性外,go需要額外對(duì)協(xié)程進(jìn)行封裝,提供給研發(fā)同學(xué)一個(gè)開箱即用的腳手架。
2、發(fā)布:封裝build邏輯,實(shí)現(xiàn)打包編譯、環(huán)境變量管理。
3、部署:是整個(gè)二進(jìn)制文件覆蓋,需要重啟服務(wù),使用熱重啟模塊,可以實(shí)現(xiàn)無損上線,以及更快的上線速度。
4、流量:CS架構(gòu)的App的接口遷移需要接入層做路由重寫,協(xié)調(diào)網(wǎng)關(guān)變更。
5、監(jiān)控:日志分級(jí),微服務(wù)間保持trace透傳,各類日志落盤符合agent采集的格式規(guī)范。
5.3 寫第一個(gè)接口
一方面,在開始技術(shù)棧遷移的時(shí)候,需要了解到go語言層面支持并發(fā),可以很輕松的開發(fā)異步程序強(qiáng)類型語言。go是強(qiáng)類型的靜態(tài)語言,編譯時(shí)確定類型,不如PHP靈活,但是更嚴(yán)謹(jǐn),更安全,可以在編譯階段檢查出來隱藏的絕大多數(shù)問題。
△類型轉(zhuǎn)換
另一方面,重構(gòu)項(xiàng)目如何治理陳舊代碼?概括的說,可以參考《重構(gòu)-改善既有代碼的設(shè)計(jì)》一書提出的 23 種代碼壞味道,有針對(duì)性地對(duì)代碼進(jìn)行重構(gòu),馴服成整潔和易于閱讀的代碼。
把前期調(diào)研和遷移策略確定好了,實(shí)際的代碼開發(fā)變得得心應(yīng)手。在遷移老接口流量的時(shí)候,我們需要在新接口用go重新實(shí)現(xiàn)一遍,調(diào)用方式上完全等同老接口,包括path、method、驗(yàn)簽、header規(guī)則、參數(shù)結(jié)構(gòu)、響應(yīng)結(jié)構(gòu)、錯(cuò)誤碼。只有應(yīng)用層上的虛擬域名不同。
5.4 質(zhì)量保障
代碼ready了,區(qū)別于php項(xiàng)目的常規(guī)測試流程,go不能繞過性能測試。因?yàn)槲覀儗憄hp幾乎不需要關(guān)注GC和內(nèi)存泄露,但是go需要,有時(shí)候手動(dòng)測試和黑盒測試是OK的,但是到線上遇到有一定并發(fā)的業(yè)務(wù)場景,就會(huì)暴露問題,常常表現(xiàn)為實(shí)例的cpu或者內(nèi)存利用率持續(xù)上漲,直至宕機(jī)。
應(yīng)對(duì)go的內(nèi)存泄露問題。一方面需要在測試流程中增加壓測環(huán)節(jié);一方面需要日常多關(guān)注一下監(jiān)控儀表盤的實(shí)例資源利用率、接口平響、穩(wěn)定性指標(biāo)是否符合預(yù)期,因?yàn)橛械碾[藏bug即使壓測也不能覆蓋到。這時(shí)需要提升go服務(wù)的可觀測性,以便及時(shí)發(fā)現(xiàn)風(fēng)險(xiǎn)。
Go質(zhì)量保障能力全景矩陣如下:
構(gòu)建線下質(zhì)量保障能力:
構(gòu)建線上質(zhì)量保障能力:
5.5 流量遷移
如上圖所示,go項(xiàng)目上線后,實(shí)際流量還在老項(xiàng)目承接。開始做流量遷移,用戶流量首先到達(dá)接入層,在這一層我們根據(jù)不同的訪問域名和路由,分流到不同的應(yīng)用層load balancer ,為了兼容老版本的App,需要在域名路由不變更的情況下,完成流量遷移。在接入層網(wǎng)關(guān)做分流,把分流到php的規(guī)則應(yīng)用到go應(yīng)用層load balancer 上,就完成了流量遷移。注意,如果是非常核心的接口,我們需要進(jìn)行灰度發(fā)布,可以采用nginx+lua的方式實(shí)現(xiàn),或者采用著名的開源網(wǎng)關(guān)ApiSix、BFE項(xiàng)目,它們都支持灰度發(fā)布。
5.6 核心功能重構(gòu)實(shí)踐
這次重構(gòu)比較突出的亮點(diǎn),體現(xiàn)在百度文庫App的全新首頁和搜索結(jié)果頁優(yōu)化。
(1)定制化新首頁
文庫用戶個(gè)性化需求較分散,希望通過將垂類用戶內(nèi)容需求共性抽象,對(duì)連續(xù)型特征且使用較高的內(nèi)容進(jìn)行提取,采用中心化集中推薦的方式,提高用戶垂類內(nèi)容結(jié)構(gòu)化滿足,進(jìn)而提升用戶留存率及續(xù)費(fèi)意愿。重構(gòu)了App首頁的布局和內(nèi)容展示。增加了個(gè)性化的『我的資料庫』,『教學(xué)進(jìn)度』,『推薦頻道』,定制化展現(xiàn)文檔榜單和文件夾榜單。
App新首頁的技術(shù)方案是全新的,重構(gòu)的動(dòng)機(jī)來自"業(yè)務(wù)驅(qū)動(dòng)",而非"質(zhì)量驅(qū)動(dòng)"。需求實(shí)現(xiàn)上,底層不依賴php老項(xiàng)目。所以直接在go項(xiàng)目開發(fā)上線,提供接口服務(wù)。這樣上線后,go自然替換掉了php原本承接的首頁流量。
△文庫新首頁
(2)搜索結(jié)果頁優(yōu)化
服務(wù)端這邊主要重構(gòu)對(duì)象是一個(gè)搜索接口,實(shí)際開發(fā)中,和產(chǎn)品溝通是否可以下線不要的tab列表和內(nèi)化的推薦邏輯;清理多處已經(jīng)下線的AB實(shí)驗(yàn)的業(yè)務(wù)邏輯,去掉已經(jīng)推全AB實(shí)驗(yàn)的代碼判斷;優(yōu)化文檔排序算法,和產(chǎn)品、前端同學(xué)對(duì)齊當(dāng)前必須的字段,去除冗余;善用協(xié)程優(yōu)化串行邏輯。
結(jié)合前端去除懶加載代碼,圖片本地化,使用端能力緩存接口數(shù)據(jù),搭建離線包服務(wù)等技術(shù)手段,搜索結(jié)果頁優(yōu)化取得了不錯(cuò)的成果。大幅降低搜索結(jié)果頁的加載速度,安卓平均降低延遲41%;IOS平均降低延遲43% ,搜索結(jié)果點(diǎn)擊率和成交的訂單量也有一定提升。
△【新老搜索結(jié)果頁】AB實(shí)驗(yàn)時(shí)的白屏?xí)r長統(tǒng)計(jì)
06 目標(biāo)達(dá)成
從2022年8月啟動(dòng)go遷移至今,接近完成App服務(wù)端的全部流量遷移工作。
1、技術(shù)迭代:得益于go語言特性先進(jìn)、內(nèi)存管理和豐富的生態(tài),提升了代碼執(zhí)行效率、安全性和可觀測性;通過梳理業(yè)務(wù)邏輯,治理冗余,清理代碼中的壞味道,封裝公共類等方法,提升質(zhì)量效率,代碼可讀性和可維護(hù)性;
2、提升性能:一方面通過協(xié)程、通道技術(shù)改變同步阻塞的代碼執(zhí)行方式;另一方面,編譯后的二進(jìn)制文件執(zhí)行效率遠(yuǎn)高于nginx+php-cgi的網(wǎng)絡(luò)模型。平均減少了約30%的接口耗時(shí),TP90減少了35%的耗時(shí);
3、降本增效:得益于go語言高性能的特性,應(yīng)用層實(shí)例的負(fù)載能力得到提升。流量遷移后,文庫App服務(wù)端在線集群縮減了約50%的的實(shí)例數(shù)量。
07 思考與總結(jié)
1、手機(jī)App屬于CS架構(gòu)的應(yīng)用,在遷移過程中要保證老版本Client可以使用服務(wù);
2、在面對(duì)一個(gè)長期項(xiàng)目時(shí),拆解目標(biāo)是很重要的,快步試錯(cuò)即時(shí)反饋也是互聯(lián)網(wǎng)思維的一部分;
3、遷移理論無損,但需要把風(fēng)險(xiǎn)同步pm同學(xué),及時(shí)關(guān)注各業(yè)務(wù)指標(biāo),同時(shí)制定預(yù)案,保證可回滾的靈活性;
4、接口剛上線或者AB實(shí)驗(yàn)推全后,遷移的接口流量上升,要養(yǎng)成經(jīng)常觀察可用性儀表盤的習(xí)慣,及時(shí)處理http status異常的問題,避免風(fēng)險(xiǎn)擴(kuò)大化為故障。
08 結(jié)語
知而不行,是為不知;行而不知,可以致知。
回想項(xiàng)目遷移重構(gòu)的整個(gè)過程,最有意思的是做技術(shù)選型和討論流量遷移具體實(shí)行方案的起步階段,那時(shí)面對(duì)臃腫龐大的php單體項(xiàng)目如何進(jìn)行遷移,是有些迷茫的。在實(shí)踐的摸索過程中逐漸加深對(duì)項(xiàng)目的理解,通過所得的啟發(fā)來推導(dǎo)制定下一步的行動(dòng),形成正向循環(huán)。希望本文的內(nèi)容對(duì)大家的工作實(shí)踐有所幫助。
——END——
推薦閱讀:
掃光動(dòng)效在移動(dòng)端應(yīng)用實(shí)踐
Android SDK安全加固問題與分析
搜索語義模型的大規(guī)模量化實(shí)踐
如何設(shè)計(jì)一個(gè)高效的分布式日志服務(wù)平臺(tái)
視頻與圖片檢索中的多模態(tài)語義匹配模型:原理、啟示、應(yīng)用與展望文章來源:http://www.zghlxwxcb.cn/news/detail-531667.html
百度離線資源治理文章來源地址http://www.zghlxwxcb.cn/news/detail-531667.html
到了這里,關(guān)于從php5.6到golang1.19-文庫App性能躍遷之路的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!