舍棄Python+C,Salesforce將企業(yè)級軟件全面遷移到Go語言
Python 非常適合快速編寫更高級別的應用程序,但并不總是能夠提供企業(yè)級所需的高性能。C 可以創(chuàng)建高性能的可執(zhí)行文件,但是添加功能會花費更多時間。這篇文章分享了 Einstein Analytics 企業(yè)級軟件從 C-Python 混合遷移到完全使用 Go 應用程序的經(jīng)驗。
我們很少有機會直接將兩種技術彼此比較以完成同一任務。但是有時就會那么巧遇到星星排成一行的情況,比如從當前技術堆棧中你一直得到的是負面影響,而這時恰巧出現(xiàn)了滿足你確切需求的新技術,或者項目的規(guī)模和功能集超過了現(xiàn)有技術的能力范圍。
在 Salesforce,我們在過去幾年中遇到了這種情況。我們將大多數(shù) Einstein Analytics 后端從 Python-C 混合平臺移植到了 Go。Go 是 Google 為大規(guī)模現(xiàn)代軟件工程設計的一種語言。傳說中,谷歌工程師想創(chuàng)建一種為大型應用程序設計的語言,并在等待大型 C ++ 項目編譯時開始了對 Go 的設計。
這篇文章將分享了我們將企業(yè)級軟件從 C-Python 混合遷移到(幾乎)完全使用 Go 應用程序的經(jīng)驗。
Einstein Analytics 將業(yè)務智能處理添加到 Salesforce 實例中。通過基于云的 AI 處理,無論結構和格式如何,它都直接從 Salesforce CRM 數(shù)據(jù)以及盡可能多的客戶外部數(shù)據(jù)中生成可行的見解或預測、管道報告、性能度量。
在后臺,給定的 Salesforce 實例將 Einstein Analytics 功能公開為常規(guī) Salesforce REST API 的一部分。這些鏈接到一個查詢服務器集群,每個查詢服務器都提供緩存在內(nèi)存中的鏈接數(shù)據(jù)集的查詢,但是它們可以從集群中的任何節(jié)點填充緩存的數(shù)據(jù)。為了管理所有這些請求,我們在每個服務器上都有一個優(yōu)化的流程,該流程將請求路由到適當?shù)墓?jié)點,并將響應轉(zhuǎn)發(fā)到 API 請求的發(fā)起者。對于任何讀取數(shù)據(jù)集的查詢服務器,這些調(diào)用都看起來是本地的。而本地意味著快速。較大的數(shù)據(jù)集是分區(qū)的,無狀態(tài)查詢協(xié)調(diào)器聚合來自遠程分區(qū)子查詢的數(shù)據(jù)。
數(shù)據(jù)集是使用 ETL(提取,轉(zhuǎn)換,加載)創(chuàng)建的 批處理,然后以專有的列式數(shù)據(jù)庫格式存儲。最初成為 Einstein Analytics 的產(chǎn)品的查詢引擎和數(shù)據(jù)集創(chuàng)建工具是用 C 編寫的, 使用 Python 包裝器提供高級功能解析查詢、REST API 服務器、表達式引擎等等。
從本質(zhì)上講,該產(chǎn)品具有兩全其美的優(yōu)勢。Python 非常適合快速編寫更高級別的應用程序,但并不總是能夠提供企業(yè)級所需的高性能。C 可以創(chuàng)建高性能的可執(zhí)行文件,但是添加功能會花費更多時間。
轉(zhuǎn) Go 初體驗
最初,這種組合是起作用的。但是,在開發(fā)該軟件多年之后,Einstein Analytics 開始出現(xiàn)性能下降問題。這是因為不屬于核心查詢引擎的很多功能都被添加到了 Python 包裝器中。這種方式可以快速開發(fā)和部署功能,但是隨著時間的流逝,它們會拖累整個系統(tǒng)。Python 的多線程性能不是很好,因此要求包裝程序執(zhí)行的次數(shù)越多,其執(zhí)行效果就越差。
之前的團隊已經(jīng)在考慮將包裝器移植到 Go 上,因此我們也做了一些研究。我們很快意識到,在企業(yè)級系統(tǒng)上,我們將面臨另外兩個問題。首先,Python 使用松散類型輸入,這對于一個快速開發(fā)新想法并將其投入生產(chǎn)的小型團隊非常有用,但對于某些客戶為此付出數(shù)百萬美元的企業(yè)級應用程序而言,卻不太合適。其次,我們預見到一個巨大的依賴噩夢即將來臨,因為部署正確的 Python 庫、版本和文件是一件苦差事。所以在 2014 年,我們決定移植 Python 包裝器到 Go 上。
最初,我們對年輕的 Go 生態(tài)系統(tǒng)持謹慎態(tài)度,但是當我們研究過該語言的設計目標后(轉(zhuǎn)到 Google:軟件工程服務中的語言設計)),它給我們留下了深刻的印象。它是為軟件工程而設計的,而不僅僅是語言的復雜性,因此它的優(yōu)勢包括可靠的內(nèi)置工具,快速的編譯和部署以及簡單的故障排除。
企業(yè)軟件面臨的現(xiàn)實問題是,與編寫代碼相比,需要花費更多的時間閱讀代碼。我們感謝 Go 使代碼易于理解。在 Python 中,你可以編寫超級優(yōu)雅的列表推導式和幾乎是數(shù)學式的漂亮代碼。但是,如果你沒有參與編寫代碼,那么這種優(yōu)雅可能讓可讀性付出代價。
第一個項目進展順利。我們對新項目的性能和可維護性感到非常滿意。我們遇到的為數(shù)不多的抱怨之一是,在選擇可伸縮性而不是原始性能來幫助它們進行垃圾回收時,需要在語言上進行權衡:他們決定開始將原始類型作為指針而不是值存儲在接口中,這為我們帶來了性能開銷和額外的分配。
全部遷移到 Go 上
這個體驗是相當好的,以至于在 2016 年編寫具有更好的優(yōu)化程序的新查詢引擎內(nèi)核并改進我們的數(shù)據(jù)集創(chuàng)建工具時,我們決定使用 Go 進行操作。我們獲得專業(yè)知識的速度與 Go 生態(tài)系統(tǒng)成熟的速度差不多,因此減少開銷并使我們的代碼在單一語言中可重用是有意義的。另外,我們希望消除 CGO 接口的開銷。
最大的不確定因素是性能。Go 在其 Goroutines 中 使用了異步 IO 的輕量級“ 綠色線程 ”模型,它為我們提供了優(yōu)于 Python 的多線程優(yōu)勢, 但是 C 代碼運行的要多快就有多快——它用內(nèi)置的安全性來換取速度,加上 C 編譯器更成熟,有更好的優(yōu)化。我們的團隊創(chuàng)建了一個概念驗證(POC),它在性能上幾乎與 C 引擎相當,但前提是我們使用正確的編程模式:
緩沖所有 IO,以減少 Go 系統(tǒng)調(diào)用的開銷。在系統(tǒng)調(diào)用中,當前 Goroutines 會讓步于該調(diào)用。
如果可能發(fā)生緊密循環(huán),請使用結構代替接口,以最大程度地減少接口方法的間接開銷。
在緊密循環(huán)內(nèi)使用預分配的緩沖區(qū)(類似于 io.Reader 的 工作方式),以最大程度地減少垃圾收集壓力。
批量處理數(shù)據(jù)行是解決不良編譯器內(nèi)聯(lián)的一種解決方法,以使實際計算更接近數(shù)據(jù),并最大程度地減少每次函數(shù)調(diào)用的開銷。
2017 年我們完成了重寫,新的 Go 版本的 Einstein Analytics 在 2018 年正式投入使用。通過將所有內(nèi)容保持為同一語言,我們可以重用代碼并提高工作效率。跨平臺和可移植的潛力使移植代碼變得容易。如果我們需要在移動應用程序中使用任何這些代碼,則可以將其交叉編譯到 iOS 或 Android,這樣就可以正常工作了。
之前,我說過該版本(幾乎)完全用 Go 編寫。但我們的集群管理器是一個例外,它看起來似乎有些奇怪,因為 Kubernetes 和其他類型的集群協(xié)調(diào)應用程序是 Go 的最常見用法,但是負責此服務的團隊對使用 Java 感到更自在。讓團隊掌控自己的組件很重要;你不能強迫人們?nèi)プ鏊麄儾幌胱龅氖虑椤?/p>
盡管 Go 有一些必須解決的局限性,但我們對結果感到非常滿意。Go 還會繼續(xù)改進。他們通過將其移至 靜態(tài)單一分配形式 來解決其編譯器中的某些缺陷,這使得進行花式優(yōu)化變得更加容易。垃圾回收變得越來越高效,并且編譯器通常很智能,可以執(zhí)行轉(zhuǎn)義分析,以檢測何時可以廉價地在堆棧而不是堆上分配變量值。
作為開發(fā)人員,如果你想用任何語言編寫高性能代碼,你需要熟悉編譯器的工作方式。這不是語言的全部。Go 有一個非常簡單的參考文檔——只有兩頁!但是了解編譯器需要收集所有這些零散的知識,它詳細說明了你可以在所使用的特定版本的 Go 中使用的所有優(yōu)化。
經(jīng)過這些移植之后,我們的團隊在 Go 及其編譯器技術方面積累了一定的專業(yè)知識。但是仍然還是會遇到一些問題。例如,你可以很容易地將數(shù)據(jù)寫入到 更便宜的堆棧中,而不是寫入到更昂貴的堆中。僅僅通過閱讀代碼,你甚至都不知道會發(fā)生這種情況。因此,與需要高性能的任何新語言一樣,你需要密切監(jiān)視進程并創(chuàng)建有關 CPU 和內(nèi)存使用情況的基準。然后與社區(qū)分享你所學到的知識,以使這些知識變得不那么局部化。
結 論
選擇一種較新的語言并將其引入企業(yè)公司可能是一場賭博。幸運的是,Go 生態(tài)系統(tǒng)與我們一同成長。Google 繼續(xù)支持該語言發(fā)展,并已被 其他很多大型公司 接納。現(xiàn)在,我們擁有一支全職從事 Go 的工程師團隊,并且我們繼續(xù)獲得了一些積極的成果。我們期待與 Go 社區(qū)一起成長,并分享我們從經(jīng)驗中學到的更多知識。
Salesforce 相信支持 Go 之類的開源技術可以推動我們的行業(yè)向前發(fā)展,開啟新的職業(yè)生涯并建立對我們創(chuàng)建的產(chǎn)品的信任。