【轉載】捨棄 Python+C,Salesforce 將企業級軟件全面遷移到 Go 語言

最近了解了一些Salesforce的軟件,恰好看到這篇文章,從技術的角度,python和java筆者都有涉獵,轉載這篇文章跟大家分享一下python做企業軟件的不足之處。Go語言看來是以後的大勢所趨,自己應該要入手了。

首先,Python 使用鬆散類型輸入,這對於一個快速開發新想法並將其投入生產的小型團隊非常有用,但對於某些客戶爲此付出數百萬美元的企業級應用程序而言,卻不太合適。 其次,我們預見到一個巨大的依賴噩夢即將來臨,因爲部署正確的 Python 庫、版本和文件是一件苦差事。所以在 2014 年,我們決定移植 Python 包裝器到 Go 上。

在 Python 中,你可以編寫超級優雅的列表推導式和幾乎是數學式的漂亮代碼。但是,如果你沒有參與編寫代碼,那麼這種優雅可能讓可讀性付出代價。

我們很少有機會直接將兩種技術彼此比較以完成同一任務。但是有時就會那麼巧遇到星星排成一行的情況,比如從當前技術堆棧中你一直得到的是負面影響,而這時恰巧出現了滿足你確切需求的新技術,或者項目的規模和功能集超過了現有技術的能力範圍。

在 Salesforce,我們在過去幾年中遇到了這種情況。我們將大多數 Einstein Analytics 後端從 Python-C 混合平臺移植到了 Go。Go 是 Google 爲大規模現代軟件工程設計的一種語言。傳說中,谷歌工程師想創建一種爲大型應用程序設計的語言,並在等待大型 C ++ 項目編譯時開始了對 Go 的設計。

這篇文章將分享了我們將企業級軟件從 C-Python 混合遷移到(幾乎)完全使用 Go 應用程序的經驗。

Einstein Analytics 將業務智能處理添加到 Salesforce 實例中。通過基於雲的 AI 處理,無論結構和格式如何,它都直接從 Salesforce CRM 數據以及儘可能多的客戶外部數據中生成可行的見解或預測、管道報告、性能度量。

在後臺,給定的 Salesforce 實例將 Einstein Analytics 功能公開爲常規 Salesforce REST API 的一部分。 這些鏈接到一個查詢服務器集羣,每個查詢服務器都提供緩存在內存中的鏈接數據集的查詢,但是它們可以從集羣中的任何節點填充緩存的數據。爲了管理所有這些請求,我們在每個服務器上都有一個優化的流程,該流程將請求路由到適當的節點,並將響應轉發到 API 請求的發起者。對於任何讀取數據集的查詢服務器,這些調用都看起來是本地的。而本地意味着快速。 較大的數據集是分區的,無狀態查詢協調器聚合來自遠程分區子查詢的數據。

數據集是使用 ETL(提取,轉換,加載)創建的批處理,然後以專有的列式數據庫格式存儲。最初成爲 Einstein Analytics 的產品的查詢引擎和數據集創建工具是用 C 編寫的, 使用 Python 包裝器提供高級功能解析查詢、REST API 服務器、表達式引擎等等。

從本質上講,該產品具有兩全其美的優勢。Python 非常適合快速編寫更高級別的應用程序,但並不總是能夠提供企業級所需的高性能。C 可以創建高性能的可執行文件,但是添加功能會花費更多時間。

轉 Go 初體驗

最初,這種組合是起作用的。但是,在開發該軟件多年之後,Einstein Analytics 開始出現性能下降問題。這是因爲不屬於核心查詢引擎的很多功能都被添加到了 Python 包裝器中。這種方式可以快速開發和部署功能,但是隨着時間的流逝,它們會拖累整個系統。Python 的多線程性能不是很好,因此要求包裝程序執行的次數越多,其執行效果就越差。

之前的團隊已經在考慮將包裝器移植到 Go 上,因此我們也做了一些研究。我們很快意識到,在企業級系統上,我們將面臨另外兩個問題。首先,Python 使用鬆散類型輸入,這對於一個快速開發新想法並將其投入生產的小型團隊非常有用,但對於某些客戶爲此付出數百萬美元的企業級應用程序而言,卻不太合適。 其次,我們預見到一個巨大的依賴噩夢即將來臨,因爲部署正確的 Python 庫、版本和文件是一件苦差事。所以在 2014 年,我們決定移植 Python 包裝器到 Go 上。

最初,我們對年輕的 Go 生態系統持謹慎態度,但是當我們研究過該語言的設計目標後(轉到 Google:軟件工程服務中的語言設計)),它給我們留下了深刻的印象。它是爲軟件工程而設計的,而不僅僅是語言的複雜性,因此它的優勢包括可靠的內置工具,快速的編譯和部署以及簡單的故障排除。

企業軟件面臨的現實問題是,與編寫代碼相比,需要花費更多的時間閱讀代碼。我們感謝 Go 使代碼易於理解。在 Python 中,你可以編寫超級優雅的列表推導式和幾乎是數學式的漂亮代碼。但是,如果你沒有參與編寫代碼,那麼這種優雅可能讓可讀性付出代價。

第一個項目進展順利。我們對新項目的性能和可維護性感到非常滿意。 我們遇到的爲數不多的抱怨之一是,在選擇可伸縮性而不是原始性能來幫助它們進行垃圾回收時,需要在語言上進行權衡:他們決定開始將原始類型作爲指針而不是值存儲在接口中,這爲我們帶來了性能開銷和額外的分配。

全部遷移到 Go 上

這個體驗是相當好的,以至於在 2016 年編寫具有更好的優化程序的新查詢引擎內核並改進我們的數據集創建工具時,我們決定使用 Go 進行操作。 我們獲得專業知識的速度與 Go 生態系統成熟的速度差不多,因此減少開銷並使我們的代碼在單一語言中可重用是有意義的。另外,我們希望消除 CGO 接口的開銷。

最大的不確定因素是性能。Go 在其 Goroutines 中使用了異步 IO 的輕量級“ 綠色線程 ”模型,它爲我們提供了優於 Python 的多線程優勢, 但是 C 代碼運行的要多快就有多快——它用內置的安全性來換取速度,加上 C 編譯器更成熟,有更好的優化。我們的團隊創建了一個概念驗證(POC),它在性能上幾乎與 C 引擎相當,但前提是我們使用正確的編程模式:

  • 緩衝所有 IO,以減少 Go 系統調用的開銷。在系統調用中,當前 Goroutines 會讓步於該調用。
  • 如果可能發生緊密循環,請使用結構代替接口,以最大程度地減少接口方法的間接開銷。
  • 在緊密循環內使用預分配的緩衝區(類似於 io.Reader 的工作方式),以最大程度地減少垃圾收集壓力。
  • 批量處理數據行是解決不良編譯器內聯的一種解決方法,以使實際計算更接近數據,並最大程度地減少每次函數調用的開銷。

2017 年我們完成了重寫,新的 Go 版本的 Einstein Analytics 在 2018 年正式投入使用。通過將所有內容保持爲同一語言,我們可以重用代碼並提高工作效率。跨平臺和可移植的潛力使移植代碼變得容易。如果我們需要在移動應用程序中使用任何這些代碼,則可以將其交叉編譯到 iOS 或 Android,這樣就可以正常工作了。

之前,我說過該版本(幾乎)完全用 Go 編寫。但我們的集羣管理器是一個例外,它看起來似乎有些奇怪,因爲 Kubernetes 和其他類型的集羣協調應用程序是 Go 的最常見用法,但是負責此服務的團隊對使用 Java 感到更自在。讓團隊掌控自己的組件很重要;你不能強迫人們去做他們不想做的事情。

儘管 Go 有一些必須解決的侷限性,但我們對結果感到非常滿意。Go 還會繼續改進。他們通過將其移至靜態單一分配形式來解決其編譯器中的某些缺陷,這使得進行花式優化變得更加容易。垃圾回收變得越來越高效,並且編譯器通常很智能,可以執行轉義分析,以檢測何時可以廉價地在堆棧而不是堆上分配變量值。

作爲開發人員,如果你想用任何語言編寫高性能代碼,你需要熟悉編譯器的工作方式。這不是語言的全部。Go 有一個非常簡單的參考文檔——只有兩頁!但是瞭解編譯器需要收集所有這些零散的知識,它詳細說明了你可以在所使用的特定版本的 Go 中使用的所有優化。

經過這些移植之後,我們的團隊在 Go 及其編譯器技術方面積累了一定的專業知識。但是仍然還是會遇到一些問題。 例如,你可以很容易地將數據寫入到更便宜的堆棧中,而不是寫入到更昂貴的堆中。僅僅通過閱讀代碼,你甚至都不知道會發生這種情況。因此,與需要高性能的任何新語言一樣,你需要密切監視進程並創建有關 CPU 和內存使用情況的基準。然後與社區分享你所學到的知識,以使這些知識變得不那麼局部化。

結論

選擇一種較新的語言並將其引入企業公司可能是一場賭博。幸運的是,Go 生態系統與我們一同成長。Google 繼續支持該語言發展,並已被其他很多大型公司接納。現在,我們擁有一支全職從事 Go 的工程師團隊,並且我們繼續獲得了一些積極的成果。我們期待與 Go 社區一起成長,並分享我們從經驗中學到的更多知識。

Salesforce 相信支持 Go 之類的開源技術可以推動我們的行業向前發展,開啓新的職業生涯並建立對我們創建的產品的信任。

原文鏈接:

https://stackoverflow.blog/2019/10/07/how-salesforce-converted-einstein-analytics-to-go/

譯文鏈接:

https://www.infoq.cn/article/NB813Qo68KzV0PIVa9iw

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