鏈客,專爲開發者而生,有問必答!程序員
此文章來自區塊鏈技術社區,未經容許拒絕轉載。golang
本篇文章是 Go 語言 2018 年終盤點,力求客觀、深刻分析 2018 年 Go 語言的技術發展示狀,同時對明年可能的發展狀況進行預測和展望。編程
今年真可謂是不平靜的一年,前有人工智能國家級戰略的發佈,行業已經在大跨步的挺進,但人才缺口天天都在擴大;後有區塊鏈技術從爆發式增加到大幅回落,無數程序員蜂擁而至,又在現現在變得手足無措。json
那麼,Go 語言在 2018 年這一年發展得又如何呢?它的下一步又將會怎樣?且聽筆者細細道來。後端
首先,筆者要說的是,在 TIOBE 於 2018 年 11 月份公佈的編程語言排行榜中,Go 語言已然擠到了前 10 的位置。雖然這與去年同期的第 14 位看起來相差不大,但倒是一個里程碑式的進步。瀏覽器
圖 1:TIOBE Index for Nov 2018緩存
從 Google Trends 提供的流行趨勢統計來看,在過去的 12 個月裏,Go 語言的流行也是持續升溫的。服務器
圖 2: Google Trends - Golang 熱度隨時間變化的趨勢網絡
這種升溫雖然並不算快,可是很持久。這對編程語言的生態環境和人才的發展是很是有利的。數據結構
此外,徹底不出乎咱們的意料:中國依然是 Go 語言愛好者最多的國家,沒有之一。
圖 3: Google Trends - Golang 按區域顯示的搜索熱度
具備諷刺意味的是,做爲 Go 語言誕生地的美國,僅排在了第 15 位。咱們對先進技術和前沿科技的熱衷絕對是不輸他國的。下面,讓咱們再把尺度縮小到城市級別。
圖 4 :Google Trends - Golang 按區域顯示的搜索熱度(城市)
顯然,在我國,北京、深圳、上海這三個城市彙集了很是多的 Go 語言程序員和工程師。尤爲是北京,簡直是 Go 語言愛好者的聖地啊!
至於北京博得頭籌的緣由,據筆者觀察,首先確定是:在北京的互聯網公司不少,起碼明顯多於其餘的1、二線城市。Go 語言現在在互聯網公司中很是流行,即便有的公司高層並無批准大規模地使用 Go 語言,可是工程師們都在作積極的嘗試。
其次,北京作雲計算的公司不少,不管是面向市場的公有云仍是自建自用的私有云。說到雲計算,咱們就不得不說起開放平臺技術、容器技術、集羣管理技術,以及如今很火熱的微服務(Microservices)和 Serverless 技術,等等。而這些,偏偏都是 Go 語言的專長。在這些方面,有不少成熟的基於 Go 語言的解決方案可供選擇。
再次,北京的高科技創業公司很是多。他們每每沒有歷史包袱、敢於創造和嘗試。在作技術選型的時候,他們也更容易選擇 Go 語言。由於,Go 語言既擁有編譯型編程語言固有的高運行效率,又具備解釋型編程語言常有的高開發效率。並且,Go 語言還不像有些編程語言那樣時不時地出現內鬥、分裂等混亂狀況,固然也沒有無良的技術持有者吵鬧着要對編程語言的商用進行收費。
Go 語言在語言規範的發展、版本的迭代和開發者生態的建設方面都很是的穩定,並有着良好的包容性和兼容性。保持簡單、面向契約和利於協做是 Go 語言最突出的設計哲學。不管是作軟件原型,仍是用於小團隊做戰,又或是進行大規模的研發,Go 語言都會是很不錯的選擇。
最後,不少喜好 Go 語言、致力於推廣 Go 語言技術的我的開發者、技術團隊、互聯網公司以及知識服務廠商也都在北京。這都直接或間接地致使了 Go 語言在這座城市的流行。
好了,到這裏,筆者相信你已經對 Go 語言在中國的流行有了必定的瞭解。下面,咱們再來講說 Go 語言在 2018 年具體都有哪些進展。
首先說一下,關於 Go 語言在 2018 年以前的具體進展,筆者推薦你去看這幾篇同系列文章,以下:
語法和平臺
Go 語言官方團隊在 2018 年 2 月正式發佈它的 1.10 版本。不一樣於其餘不少被稱爲版本帝的編程語言,到了這樣一個版本號 10,Go 1 在語言規範方面已經幾乎沒有什麼改動了,一些語法上的小小加強也並不值得咱們特別關注。而在 2018 年 8 月發佈的 Go 1.11 更是沒有任何語言規範方面的變更。
Go 語言對於自己的向後兼容性保持得很是好,高版本對低版本中的語言語法、工具和標準庫都不會有任何破壞。然而,Go 語言在其支持的操做系統方面仍是很大刀闊斧的。這體如今,Go 1.10 再也不支持 10.3 如下版本的 FreeBSD 和 8.0 如下版本的 NetBSD。而且,這個版本也是支持 OpenBSD 6.0、OS X 10.9 以及 Windows XP 和 Windows Vista 的最後一個版本。在這些操做系統之上編寫或運行 Go 語言程序的開發者們要注意。
環境和工具
使用過 Go 語言的開發者們都知道,當把 Go 語言的預編譯包解壓到某個目錄後,咱們還須要至少設置兩個環境變量——GOROOT 和 GOPATH。前者表明直接包含 Go 語言自己的那個目錄路徑,然後者則用於指定可放置第三方庫和自有代碼的工做區(或者說工做目錄)的路徑。
一個好消息是,自 Go 語言的 1.10 版本起,GOROOT 這個環境變量就沒有必要設置了。若是咱們不設置它,那麼 Go 的標準工具會嘗試以自身所在的目錄爲基礎,自動地推斷出 GOROOT 應該指向的目錄路徑。
另外,從這個版本開始,咱們能夠自行地設定 Go 語言的臨時目錄路徑了,設定的途徑是設置環境變量 GOTMPDIR。Go 語言的臨時目錄主要用於存放 Go 工具在編譯或測試程序時產生的各類臨時文件。在這以前,這些臨時文件都會被存放到固定的地方,此地的具體路徑會根據操做系統的不一樣而不一樣,通常會位於操做系統的臨時目錄的某個子目錄下。自定義這個目錄的好處在於,可讓咱們方便地觀察編譯過程,並查看編譯或測試的中間結果。
說到編譯,筆者必定要提一下 1.10 版本的另外一項改進,這與 go build 命令有關。之前,若是咱們要強行地從新構建全部相關的代碼包,那麼就須要在運行這個命令的時候追加標記「-a」。而如今,咱們無需這樣作了。go build 命令會根據源碼文件內容、構建標記和編譯元數據,自動地決定何時應該從新構建那些代碼包。這項工做不再須要人工干預了。
與此項改進相關的變化是,go build 命令如今老是會把最近的構建結果緩存起來,以便在未來的構建中重用。咱們能夠經過運行 go env GOCACHE 命令來查看緩存目錄的路徑。緩存的數據老是可以正確地反映出當時的源碼文件、構建環境和編譯器選項等的真實狀況。一旦有任何變更,緩存數據就會失效,go build 命令就會再次真正地執行構建。所以,咱們並不用擔憂緩存數據體現的不是實時的結果。實際上,這正是上述改進可以有效的主要緣由。go build 命令會按期地刪除最近未使用的緩存數據,但若是你想手動刪除全部的緩存數據,運行一下 go clean -cache 命令就行了。
順便說一下,對於測試成功的結果,go 命令也是會緩存的。運行 go clean -testcache 命令將會刪除掉全部的測試結果緩存。不過別擔憂,這樣作確定不會刪除任何的構建結果緩存,它們是兩碼事。
此外,設置環境變量 GODEBUG 的值也能夠稍稍地改變 go 命令的緩存行爲。好比,設置值爲 gocacheverify=1 將會致使 go 命令繞過任何的緩存數據,而真正地執行操做並從新生成全部結果,而後再去檢查新的結果與現有的緩存數據是否一致。
再來講 go install 命令。如今,go install 命令在默認狀況下只會去安裝咱們明確指定的那些代碼包。這些代碼包依賴的那些包並不會被安裝。這一樣得益於構建結果緩存,它可使安裝的速度獲得明顯的提高。若是你想要強制地安裝依賴包,那麼請在運行命令的時候追加「-i」標記。
程序測試
前面咱們說過了,測試成功的結果也會被緩存。若是 go test 命令肯定可使用被緩存的結果,那麼它打印出的內容也會出自於緩存。這時,被打印的內容中會包含「(cached)」字樣。
另外,go test 命令如今會自動地運行 go vet 命令,以便在真正運行測試以前識別出一些程序編寫方面的問題。咱們都知道,go vet 命令用於對 Go 語言源碼進行靜態檢查,並報告已發現的可疑問題。這些問題通常都是符合語法規則的,所以編譯器沒法查出它們。可是,它們頗有可能表明了對某些程序實體(或者說 API)的錯誤使用。雖然 go vet 命令有時候並不能保證它報告的每個問題都是真正的問題,但它卻能夠給予咱們一份重要的參考,以便讓咱們在編程的過程當中當心行事。
與 Go 語言提供的不少高級功能同樣,咱們也能夠阻止 go test 命令自動運行 go vet 命令,這須要在運行前者的時候追加「-vet=off」這個標記。
最後,關於 go test 命令,還有兩個值得注意的新標記——「-failfast」和「-json」。顧名思義,「-failfast」標記可讓 go test 命令一旦發現有測試失敗的狀況就當即忽略掉剩餘的測試並終止運行。不過要注意,若是存在與失敗的測試併發進行的測試的話,那麼後者仍是會繼續運行直至完成的。「-json」標記對於程序測試的自動化大有裨益。它會讓 go test 命令產生 JSON 格式的測試報告,這使得其餘程序很容易讀入和處理。
程序文檔
關於程序文檔,只有一點須要咱們注意。Go 1.11 是 godoc 命令支持命令行接口的最後一個版本。** 在將來的版本中,咱們運行 godoc 命令的時候,它會啓動一個 Web 服務器,以便讓咱們直接進入圖形化界面進行文檔查詢。
程序性能分析
如今,runtime/pprof 代碼包中的 Lookup 函數已經支持了更加多樣的參數值。這就意味着,Go 語言的程序性能分析如今能夠生成和解讀更多視角下的分析報告了。咱們能夠把這樣的分析報告包含的內容叫作程序性能概要信息(簡稱概要信息),並把存儲這些分析報告的文件叫作概要文件。
Lookup 函數能夠生成的概要信息目前共有 6 種。這 6 種概要信息分別由字符串類型的參數值 goroutine、heap、allocs、threadcreate、block 和 mutex 表明。下面是它們表明的含義:
goroutine:收集當前正在使用的全部 goroutine 的堆棧跟蹤信息。
heap:收集與堆內存的分配和釋放有關的採樣信息,默認以在用空間(inuse_space)的視角呈現。
allocs:一樣收集與堆內存的分配和釋放有關的採樣信息,但默認以已分配空間(alloc_space)的視角呈現。
threadcreate:收集一些特定的堆棧跟蹤信息,其中的調用鏈上的代碼都致使了新的操做系統線程的產生。
block:收集因爭用同步原語而被阻塞的那些代碼的堆棧跟蹤信息。
mutex:曾經做爲同步原語持有者的那些代碼的堆棧跟蹤信息。
這裏所說的同步原語,指的是存在於 Go 語言運行時系統內部的一種底層同步工具,或者說一種同步機制。它是直接面向內存地址的,並以異步信號量和原子操做做爲實現手段。咱們已經熟知的通道、互斥鎖、條件變量、「WaitGroup」以及 Go 語言運行時系統自己,都會利用它來實現本身的功能。
另外,在用空間和已分配空間的區別是,前者指的是已經分配但尚未被回收的空間,然後者只關注分配出的空間,不論它們是否已經被回收。
注意,若是咱們在運行 go test 命令的時候追加了標記「-memprofile」,那麼該命令會經過底層的 API 以 allocs 爲視角生成概要信息和概要文件。這至關於對從測試開始時的全部已分配字節進行記錄,包含已經被垃圾回收器收回的那些字節。在 Go 1.11 版本以前,go test 命令在這種狀況下采用的是 heap 視角。
最後,go tool pprof 工具已經能夠正確地單獨讀取和處理全部種類的概要文件了。這得益於,從 Go 1.10 版本開始,block 和 mutex 視角下的概要信息已經完善。在這以前,咱們使用 go tool pprof 查閱這兩種概要文件的時候,還不得不一樣時指定相應程序的二進制文件。
運行時系統
須要特別注意,runtime 代碼包中的 LockOSThread 函數和 UnlockOSThread 函數的行爲已經發生了變化。咱們都知道,前一個函數的功能是將當前的 goroutine 與那一時刻正在承載這個 goroutine 運行的操做系統線程進行綁定。在綁定以後,這個 goroutine 就只能由該操做系統線程運行了,反之,該操做系統線程也只能運行這一個 goroutine 了。顯而易見,runtime. UnlockOSThread 函數的功能是解除上述綁定關係。固然了,這兩個函數都只能做用於它們被調用時所在的那個 goroutine。
之前,runtime. LockOSThread 函數是冪等的。也就是說,不管咱們在同一個 goroutine 中調用了它多少次,都只至關於調用了一次。另外一方面,只要咱們調用一次 runtime. UnlockOSThread 函數,就老是可以解除針對於當前 goroutine 的這種綁定。
可是,從 Go 語言的 1.10 版本開始,在咱們想要徹底解除綁定的時候,可能就須要調用屢次 runtime. UnlockOSThread 函數纔可以實現了。至於具體須要調用多少次徹底取決於,當初在同一個 goroutine 中調用 runtime. LockOSThread 函數的次數。換句話說,只有進行相同次數的函數調用,才能讓當前 goroutine 與某個操做系統線程之間的綁定關係徹底解除。咱們能夠把如今的這種對應關係理解爲是基於嵌套的,能夠想象一下:當初包裝了多少層紙箱,如今就要拆開多少層紙箱。
其實一直以來,有不少第三方 Go 語言庫的做者都誤覺得對於這兩個函數的調用就是基於嵌套關係的。不過不管怎樣,咱們如今都應該仔細檢查代碼並當心的應對了。
筆者認爲,若是你確實須要進行這種綁定,那麼就應該基於這兩個函數封裝一個數據結構。在這個數據結構中,至少應該包含一個用於記錄調用 runtime. LockOSThread 函數次數的字段,以方便後續的解綁操做。
在 2018 年,對於 Go 語言的運行時系統來講,咱們能夠輕易感知到的變化基本上只有這一個。不過,很是多的改進和優化都在悄無聲息的進行着,有的已經完成了,而有的還在進展之中。已完成的改進如:在一般狀況下,咱們傳遞給 runtime.GOMAXPROCS 函數的參數值已經再也不受限了,只要它在 int32 類型可容納的範圍以內就能夠。
標準庫
在 Go 語言的 1.10 和 1.11 這兩個版本中,官方團隊與社區開發者們一塊兒對標準庫作了大量的改進。可喜可賀,社區開發者對 Go 語言的貢獻次數如今已經超過官方團隊了!
因爲這方面的改進繁多,也因爲筆者在新近發佈的極客時間專欄《Go 語言核心 36 講》中已經詳細講解了很多,因此這裏就再也不贅述了。
兩個新實驗
咱們再來講說 Go 1.11 的兩個新實驗吧,一個是對 WebAssembly 的實驗性支持,另外一個是推出由 dep 和 vgo 演化而來的依賴管理機制和新概念 module。
按照官方的描述,WebAssembly(縮寫爲 WASM)是一種二進制指令格式,它針對的是以堆棧爲基礎的虛擬機。WASM 有很好的可移植性,以便讓 C++、Golang、Rust 等高級編程語言來操控它,並有能力部署到 Web 程序上。
用普通話來講,WASM 提供了一種途徑,可讓咱們用後端編程語言直接去編寫 Web 頁面中的邏輯。在 Go 1.11 中,咱們能夠很輕易地把 Go 語言源碼文件轉換爲 WASM 格式的文件,而後在 Web 頁面中經過寥寥幾行 Java 代碼引用這個文件並把其中的邏輯發佈到頁面上。WASM 的 1.0 版本如今已經支持了絕大多數的主流網絡瀏覽器,好比:Chrome、Firefox、Safari 等。若是想了解具體的玩法,你能夠參看這個 wiki 頁面。
筆者對 Go 語言官方的這種探索性實驗一直都持同意的態度,不管是前些年的移動端(Android 和 iOS)方向,仍是今年的 Web 端(WASM)方向。不過,筆者依然以爲 Go 語言的優點在服務端,如今很明顯,並且在可預見的將來也應該是如此。因此,對於這些多端探索,筆者建議你們「保持關注,積極試驗,但不要偏移重心」。
相比之下,筆者卻是更加看好 Go 語言新放出的依賴管理機制。Go 語言愛好者們都知道,Go 語言在這方面一直是缺失的。雖然目前存在幾個不錯的第三方解決方案,可是沒有一個是能夠脫穎而出的,同時官方也一直沒有給出一個統一的標準。
通過了一段時間的試驗和演化,Go 語言官方的依賴管理機制終於脫胎於 dep 和 vgo。雖然其間存在一些摩擦和風波,可是結果終歸是積極的。
在 Go 語言新的依賴管理機制中,module 是一個很是重要的概念。簡單來講,module 象徵着由某個 Go 語言代碼包以及它依賴的代碼包共同組成的一個獨立單元。這裏的 Go 語言代碼包和它依賴的那些代碼包都是版本化的。一個 module 的根目錄下老是直接存有一個名爲 go.mod 的文件。這個文件中會包含當前 module 的路徑,以及它依賴的那些 module 的路徑和版本號。如此一來,對於每個版本的 module,它依賴的全部代碼都會被固化下來。這對於後續的版本管理和 module 重建來講都是重要的基礎。詳情能夠參看這裏的 wiki 頁面。
不過,不要忘了,Go 1.11 中包含的這個依賴管理機制是實驗性的。其中的任何部分都有可能因爲社區的反饋和官方的改進而變化。因此,你在正式使用它以前必定要考慮到後續可能存在的變動成本。雖然如此,筆者仍然會鼓勵廣大開發者們去積極使用和反饋。想象一下 maven 對於 Java 世界的重要性吧。筆者相信,咱們心目中的 Go 項目依賴管理機制已經離此不遠了。
Go 1.12
筆者首先盼望的確定是 Go 語言依賴管理機制的第一個穩定版,而且相信不少 Go 語言愛好者都是如此。可是,在筆者看來,這個穩定版本並不必定就會在 Go 語言的 1.12 版本中發佈,雖然目標是這樣的。
正如前文所述,Go 1.12 會從 godoc 命令中去掉命令行接口,而只保留基於 Web 的圖形化查詢界面。同時,它也再也不容許開發者經過 GOCACHE 環境變量去禁用構建結果緩存。固然了,這個版本也會包含大量針對標準庫的改進,詳細內容能夠到此版本的發佈說明草稿中查看。
https://tip.golang.org/doc/go...
Go 2
在去年咱們就說過,Go 語言官方已經把 Go 2 的計劃鄭重地擺上了桌面。今年的進展是,Go 2 的設計草案已經發布了。
Go 語言做者之一 Robert Griesemer 不久前剛剛在官方博客發文稱,Go 2 已經選擇出備選新特性提案,進入提案反饋階段,他呼籲社區積極參與進來,和官方團隊一塊兒改進 Go 語言設計。具體詳情能夠看 InfoQ 的報道《Go 2 提上日程,官方團隊呼籲社區給新特性提案提交反饋》
目前來看,Go 2 將會主要解決三個問題,即:錯誤處理、錯誤值以及對泛型自定義的支持。
從多年前開始,不少 Go 程序開發者就已經在抱怨 Go 語言在錯誤處理方面的醜陋了。Go 函數的多返回值使咱們能夠在返回通常結果值的同時攜帶錯誤值。這是一個很亮眼的特性,可讓咱們重視錯誤,並老是進行明確的處理。不過,這也帶來了一個問題。咱們在調用這樣的 Go 函數以後,不得不先用 if 語句檢查錯誤值是否爲 nil,而後才能進行下一步處理。若是在咱們的程序中有不少這樣的代碼,那麼顯然是很醜陋的。
不過,筆者認爲,這不少都是開發者在程序設計方面存在問題致使的。然而,咱們也並不可否認,Go 語言的這種錯誤處理方式是不少程序變得醜陋的導火索。不論怎樣,Go 語言官方已經開始正視這個問題並在着手解決了。
與之相關的一個問題就是錯誤值的設計。咱們知道,只要實現了 error 接口的數據類型就均可以被稱爲錯誤類型,它們的值就能夠被稱爲錯誤值。創造一個錯誤值的方式有不少,調用 errors.New 函數、調用 fmt.Errorf 函數,以及使用值的字面量,等等。這偏偏使咱們在對錯誤種類作判斷的時候不得不仔細地選擇判斷方式,是檢查錯誤值的類型?仍是判斷它是否等於某個已存在的錯誤值?又或者是對錯誤描述進行匹配?這顯然增長了錯誤處理的成本。從草案上來看,已經有一些顯著的成果了,咱們仍是拭目以待吧。
關於泛型,筆者並不想多說。容許泛型的自定義顯然能夠加強編程語言的表達能力,而且在一些場景下能夠顯著地減小重複的代碼。不過,怎樣將它設計好,並用優雅的方式展示出來,是一個很複雜的問題。在相應的草案中,Go 語言官方給出了一個看起來還不錯的方案,可是依然可能存在變數。但願官方可以參考 C++、Java、Rust、Swift 等編程語言的設計,取其精華、去其糟粕吧。
關於詳細的 Go 2 設計草案,你們能夠到這裏查閱。
https://go.googlesource.com/p...
社區與環境
筆者在今年明顯的感受到,關注 Go 語言的各路人馬又變多了。這體如今了幾個方面。首先,以 Go 語言爲主題的 meetup 明顯增多。不管是哪一個技術組織發起的,參與的人都不在少數。並且,這樣的 meetup 已經在更多的1、二線城市中出現了。
其次,互聯網上的 Go 語言中文資料(好比博客、教程、電子書等)也明顯增多,不管是免費的仍是收費的,雖然水平各不相同,可是顯然你們都在進行積極的探索和分享。
最後,不少主打技術培訓的公司和組織都已經對 Go 語言進行了重點的關注,並開發出了本身的培訓產品或服務,包括線上的知識付費產品、線下的面授課程,以及目標各有不一樣的開源項目,等等。筆者也有幸參與其中,並在極客時間開設了專欄《Go 語言核心 36 講》。
不過,隨着 Go 語言逐漸獲得各方的普遍關注,盜版和抄襲也日益猖獗。筆者在這裏呼籲,但願你們可以尊重原創做者的辛勤勞動和知識產權,拒絕盜版、抵制抄襲!只有這樣纔可以讓做者們更加積極地產出優秀的內容,咱們的學習環境才能更美好,技術社區才能所以向着健康、壯大的方向發展。
以上,就是我對 Go 語言在 2018 年發展的簡要回顧和對其將來發展的展望。但願可以藉此促使你們對 Go 語言和咱們國內的技術社區有更多的關注。