標籤:float 爆發 反饋 images 成熟 計算 等等 工作 .com
根據維基百科資料統計(https://en.wikipedia.org/wiki/List_of_programming_languages)
一共有 600 餘種不同的程式設計語言。對於五花八門的開發語言,每門語言都有自己的優缺點。而勵志成為一名博學多廣的開發人員,術業也有專攻,怎奈何也不會學習完百餘種語言。
在這些語言中,Java 作為 22 年的常青藤,無論是新爆發的 Kotlin 還是 C、C++、Python、JavaScript 等老×××語言想要在短期之間撼動其地位也是不大現實,相信這一點毋庸置疑。吃完I安從近幾個月的 TIOBE 程式設計語言熱門排行榜來看,繼 Java 之後,C、C++、C# 使用率雖有所下降,但依舊穩坐熱門排行榜的前五名。
除了以上四種頭×××語言,還有一種即 Python(蟒蛇)。隨著人工智慧、物聯網、資料科學等領域的興起,Python 迅速升溫,成為諸多開發人員的首選入門語言。然而就在一片追隨聲中,不少開發人員從 Python 轉向了 Go 語言,這究竟是什麼原因?接下來,來自 Stream 團隊的 Thierry Schellenbach 給出了九大理由,並對比了 Python,解析優缺點,希望對更多的開發人員有所助益。
為什麼開始使用 Go 語言?
原因 1 ——效能
Go 極其地快,其效能與 Java 或 C++ 相似。在我們的使用中,Go 一般比 Python 要快 30 倍。以下是 Go 與 Java 之間的基準比較:
原因 2:語言效能很重要
對很多應用來說,程式設計語言只是簡單充當了其與資料集之間的膠水。語言本身的效能常常無關輕重。
但是 Stream 是一個 API 供應商,服務於世界 500 強以及超過 2 億的終端使用者。數年來我們已經最佳化了 Cassandra、PostgreSQL、Redis 等等,然而最終抵達了所使用語言的極限。
Python 非常棒,但是其在序列化/去序列化、排序和彙總中表現欠佳。我們經常會遇到這樣的問題:Cassandra 用時 1ms 檢索了資料,Python 卻需要 10ms 將其轉化成對象。
原因 3:開發人員效率&不要過於創新
看一下絕佳的入門教程《開始學習 Go 語言》(http://howistart.org/posts/go/1/)
中的一小段代碼:
package main
type openWeatherMap struct{}
func (w openWeatherMap) temperature(city string) (float64, error) {
resp, err := http.Get("http://api.openweathermap.org/data/2.5/weather?APPID=YOUR_API_KEY&q=" + city) if err != nil { return 0, err } defer resp.Body.Close() var d struct { Main struct { Kelvin float64 `json:"temp"` } `json:"main"` } if err := json.NewDecoder(resp.Body).Decode(&d); err != nil { return 0, err } log.Printf("openWeatherMap: %s: %.2f", city, d.Main.Kelvin) return d.Main.Kelvin, nil
如果你是一個新手,看到這段代碼你並不會感到吃驚。它展示了多種賦值、資料結構、指標、格式化以及內建的 HTTP 庫。
當我第一次編程時,我很喜歡使用 Python 的高階功能。Python 允許你創造性地使用正在編寫的代碼,比如,你可以:
在代碼初始化時,使用 MetaClasses 自行註冊類別
置換真假
添加函數到內建函數列表中
通過奇妙的方法重載運算子
毋庸置疑這些代碼很有趣,但也使得在讀取其他人的工作時,代碼變得難以理解。
Go 強迫你堅持打牢基礎,這也就為讀取任意代碼帶來了便利,並能很快搞明白當下發生的事情。
注意:當然如何容易還是要取決於你的使用案例。如果你要建立一個基本的 CRUD API,我還是建議你使用 Django + DRF,或者 Rails。
原因 4:並發性&通道
Go 作為一門語言致力於使事情簡單化。它並未引入很多新概念,而是聚焦於打造一門簡單的語言,它使用起來異常快速並且簡單。其唯一的創新之處是 goroutines 和通道。Goroutines 是 Go 面向線程的輕量級方法,而通道是 goroutines 之間通訊的優先方式。
建立 Goroutines 的成本很低,只需幾千個位元組的額外記憶體,正由於此,才使得同時運行數百個甚至數千個 goroutines 成為可能。你可以藉助通道實現 goroutines 之間的通訊。Go 已耗用時間可以表示所有的複雜性。Goroutines 以及基於通道的並發性方法使其非常容易使用所有可用的 CPU 核心,並處理並發的 IO——所有不帶有複雜的開發。相較於 Python/Java,在一個 goroutine 上運行一個函數需要最小的樣板代碼。你只需使用關鍵詞「go」添加函數調用:
package main
import (
"fmt" "time"
)
func say(s string) {
for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) }
}
func main() {
go say("world") say("hello")
}
https://tour.golang.org/concurrency/1
Go 的並發性方法非常容易上手,相較於 Node 也很有趣;在 Node 中,開發人員必須密切關注非同步代碼的處理。
並發性的另一個優質特性是競賽檢測器,這使其很容易弄清楚非同步代碼中是否存在競態條件。下面是一些上手 Go 和通道的很好的資源:
https://gobyexample.com/channels
https://tour.golang.org/concurrency/2
http://guzalexander.com/2013/12/06/golang-channels-tutorial.html
https://www.golang-book.com/books/intro/10
https://www.goinggo.net/2014/02/the-nature-of-channels-in-go.html
原因 5:快速的編譯時間
當前我們使用 Go 編寫的最大微服務的編譯時間只需 6 秒。相較於 Java 和 C++獃滯的編譯速度,Go 的快速編譯時間是一個主要的效率優勢。我熱愛擊劍,但是當我依然記得代碼應該做什麼之時,事情已經完成就更好了。
Go 之前的代碼編譯
原因 6:打造團隊的能力
首先,最明顯的一點是:Go 的開發人員遠沒有 C++和 Java 等舊語言多。據知,有 38% 的開發人員瞭解 Java,19.3% 的開發人員瞭解 C++,只有 4.6% 的開發人員知道 Go。GitHub 資料表明了相似的趨勢:相較於 Erlang、Scala 和 Elixir,Go 更為流行,但是相較於 Java 和 C++ 就不是了。
幸運的是 Go 非常簡單,且易於學習。它只提供了準系統而沒有多餘。Go 引入的新概念是「defer」聲明,以及內建的帶有 goroutines 和通道的並發性管理。正是由於 Go 的簡單性,任何的 Python、Elixir、C++、Scala 或者 Java 開發人員皆可在一月內組建成一個高效的 Go 團隊。
原因 7:強大的生態系統
對我們這麼大小的團隊(大約 20 人)而言,生態系統很重要。如果你需要重做每塊功能,那就無法為客戶創造收益了。Go 有著強大的工具支援,面向 Redis、RabbitMQ、PostgreSQL、Template parsing、Task scheduling、Expression parsing 和 RocksDB 的穩定的庫。
Go 的生態系統相比於 Rust、Elixir 這樣的語言有很大的優勢。當然,它又略遜於 Java、Python 或 Node 這樣的語言,但它很穩定,而且你會發現在很多基礎需求上,已經有高品質的檔案包可用了。
原因 8:GOFMT,強制代碼格式
Gofmt 是一種強大的命令列功能,內建在 Go 的編譯器中來規定代碼的格式。從功能上看,它類似於 Python 的 autopep8。格式一致很重要,但實際的格式標準並不總是非常重要。Gofmt 用一種官方的形式規格代碼,避免了不必要的討論。
原因 9:gRPC 和 Protocol Buffers
Go 語言對 protocol buffers 和 gRPC 有一流的支援。這兩個工具能一起友好地工作以構建需要通過 RPC 進行通訊的微伺服器(microservices)。我們只需要寫一個清單(manifest)就能定義 RPC 調用發生的情況和參數,然後從該清單將自動產生伺服器和用戶端代碼。這樣產生代碼不僅快速,同時網路佔用也非常少。
從相同的清單,我們可以從不同的語言產生用戶端代碼,例如 C++、Java、Python 和 Ruby。因此內部通訊的 RESET 端點不會產生分歧,我們每次也就需要編寫幾乎相同的用戶端和伺服器代碼。
使用 Go 語言的缺點
缺點 1:缺少架構
Go 語言沒有一個主要的架構,如 Ruby 的 Rails 架構、Python 的 Django 架構或 PHP 的 Laravel。這是 Go 語言社區激烈討論的問題,因為許多人認為我們不應該從使用架構開始。在很多案例情況中確實如此,但如果只是希望構建一個簡單的 CRUD API,那麼使用 Django/DJRF、Rails Laravel 或 Phoenix 將簡單地多。
缺點 2:錯誤處理
Go 語言通過函數和預期的調用代碼簡單地返回錯誤(或返回呼叫堆疊)而協助開發人員處理編譯報錯。雖然這種方法是有效,但很容易丟失錯誤發生的範圍,因此我們也很難向使用者提供有意義的錯誤資訊。錯誤包(errors package)可以允許我們添加返回錯誤的上下文和堆棧追蹤而解決該問題。
另一個問題是我們可能會忘記處理報錯。諸如 errcheck 和 megacheck 等靜態分析工具可以避免出現這些失誤。雖然這些解決方案十分有效,但可能並不是那麼正確的方法。
缺點 3:軟體包管理
Go 語言的軟體包管理絕對不是完美的。預設情況下,它沒有辦法制定特定版本的依賴庫,也無法建立可複寫的 builds。相比之下 Python、Node 和 Ruby 都有更好的軟體包管理系統。然而通過正確的工具,Go 語言的軟體包管理也可以表現得不錯。
我們可以使用 Dep 來管理依賴項,它也能指定特定的軟體包版本。除此之外,我們還可以使用一個名為 VirtualGo 的開源工具,它能輕鬆地管理 Go 語言編寫的多重專案。
Python vs Go
我們實施的一個有趣實驗是用 Python 寫排名 feed,然後用 Go 改寫。看下面這種排序方法的樣本:
{
"functions": { "simple_gauss": { "base": "decay_gauss", "scale": "5d", "offset": "1d", "decay": "0.3" }, "popularity_gauss": { "base": "decay_gauss", "scale": "100", "offset": "5", "decay": "0.5" } }, "defaults": { "popularity": 1 }, "score": "simple_gauss(time)*popularity"
}
Python 和 Go 代碼都需要以下要求從而支援上面的排序方法:
解析得分的表達。在此樣本中,我們想要把 simple_gauss(time)*popularity 字串轉變為一種函數,能夠把 activity 作為輸入然後給出得分作為輸出。
在 JSON config 上建立部分函數。例如,我們想要「simple_gauss」調用「decay_gauss」,且帶有的索引值對為"scale": "5d"、"offset": "1d"、"decay": "0.3"。
解析「defaults」配置,便於某個領域沒有明確定義的情況下有所反饋。
從 step1 開始使用函數,為 feed 中的所有 activity 打分。
開發 Python 版本排序代碼大約需要 3 天,包括寫代碼、測試和建立文檔。接下來,我麼花費大約 2 周的時間最佳化代碼。其中一個最佳化是把得分表達 simple_gauss(time)*popularity 轉譯進一個抽象文法樹。我們也實現了 caching logic,之後會預先計算每次的得分。
相比之下,開發 Go 版本的代碼需要 4 天,但之後不需要更多的最佳化。所以雖然最初的開發上 Python 更快,但 Go 最終需要的工作量更少。此外,Go 代碼要比高度最佳化的 python 代碼快了 40 多倍。
以上只是我們轉向 Go 所體驗到的一種好處。當然,也不能這麼做比較:
該排序代碼是我用 Go 寫的第一個項目;
Go 代碼是在 Python 代碼之後寫的,所以提前理解了該案例;
Go 的表達解析庫品質優越。
Elixir vs Go
我們評估的另一種語言是 Elixir。Elixir 建立在 Erlang 虛擬機器上。這是一種迷人的語言,我們之所以想到它是因為我們組員中有一個在 Erlang 上非常有經驗。
在使用案例中,我們觀察到 Go 的原始效能更好。Go 和 Elixir 都能很好地處理數千條並行需求,然而,如果是單獨的要求,Go 實際上更快。相對於 Elixir,我們選擇 Go 的另一個原因是生態系統。在我們需求的組件上,Go 的庫更為成熟。在很多案例中,Elixir 庫不適合產品使用。同時,也很難找到/訓練同樣使用 Elixir 的開發人員。
結論
Go 是一種非常高效的語言,高度支援並發性。同時,它也像 C++和 Java 一樣快。雖然相比於 Python 和 Ruby,使用 Go 建立東西需要更多的時間,但在後續的代碼最佳化上可以節省大量時間。在 Stream,我們有個小型Team Dev為 2 億終端使用者提供 feed 流。對新手開發人員而言,Go 結合了強大的生態系統、易於上手,也有超快的表現、高度支援並發性,富有成效的編程環境使它成為了一種好的選擇。Stream 仍舊使用 Python 做個人化 feed,但所有效能密集型的代碼將會用 Go 來編寫。
我為什麼放棄了 Python ,選擇了 Go?