說說Golang的使用心得

Concurrency 的部落格發表於2015-02-28

13年上半年接觸了Golang,對Golang十分喜愛。現在是2015年,離春節還有幾天,從開始學習到現在的一年半時間裡,前前後後也用Golang寫了些程式碼,其中包括業餘時間的,也有產品專案中的。一直有想法寫點Golang相關的總結或者感想,決定還是在年前總結下吧。註明下:我只是Golang的喜好者,不是腦殘粉,也無意去挑起什麼語言之爭。

特性少,語法簡單。GO是崇尚極簡主義的,提倡少即是多。這點在它的Spec上尤其凸顯,一下午的時間絕對可以看完。GO的特性很少,很多GO的使用者都反饋,GO的關鍵字至少完全可以記在大腦裡。同時它的語法極為簡單,而且語義清晰。

部署方便。GO是一個強型別靜態語言,可以把程式碼編譯為本地機器指令。它的RUNTIME是會在編譯時一起連結到執行檔案中,這也就意味著我們不需要像JAVA那樣裝一個JVM。而且編譯出的執行檔案本身不依賴於其他動態庫,完全可以做到輕鬆的釋出。當然,如果你用GO編寫了呼叫一些動態庫介面的程式碼,那麼還是需要根據實際情況來部署這個動態庫的。這點在很多從python/java轉到go的朋友來說,非常喜歡。

有較完善的標準庫並且較為健壯。GO自身帶的標準庫是比較全面的,從檔案歸檔、壓縮、加密、資料庫到資料序列化,字元格式化、校驗和以及網路庫、同步庫等應有盡有。基本上能夠滿足很多基本的需求了。更好的是,這些標準庫的質量都非常高,都很健壯。介面也較為簡單,有清晰的文件說明。同時隨著這兩年的發展,GO的第三方庫也多了起來,雖然可能沒有像python那麼多,但是較其發展時間來說,還是非常不錯的。

整合測框架。在之前用C++寫程式碼時,寫單元測試不是個容易的工作。需要一些技巧和努力才可以做起來。但是在GO中整合了單元測試框架,只要原始碼檔案以_test.go結尾,就可以直接通過go test執行單元測試。同時還提供了程式碼測試覆蓋率工具,可以很容易實施自動化測試。除此之外,還整合了基準測試框架功能,可以很容易的測量自己寫的函式的執行效率。另外,還有效能剖析器,可以在執行時,測試時剖析程式的瓶頸點,進而可以進行優化。

健全的程式碼風格與檢查工具。當初學GO的時候,很多文章和書都會提到go fmt這個命令,統一了程式碼風格。我覺著這點實在是解決了風格之爭了。帶來的影響就是別人寫的程式碼感覺也是自己寫的一樣。還有golint,可以按照go team的風格和要求來寫程式碼。還有go vet可以用來檢查一些在GO中很隱蔽的坑。

簡單卻強大的包管理。GO的包管理可能在很多其他語言的包管理看來太弱了,但是在我看來,它解決了我需要的兩個問題,一個是迴圈依賴問題,GO是拒絕有迴圈依賴關係的包;二是包的初始化,每個包的檔案都可以實現一個init函式,用來在匯入時執行。這點在分工合作時非常有用。

容易編寫跨平臺程式碼。如果你用純GO,不牽扯到CGO的話,你可以非常容易的做到跨平臺。只需要在檔案字尾.go前,引入_linux,_windows,_x86,_x64等字元為檔名字首的結尾就可以做到只在對應的平臺中編譯。GO還有build constraints控制程式碼在什麼條件下編譯。如果你用到了CGO,就牽扯到了C的跨平臺問題,所以稍微麻煩那麼一些,但是問題也不是太大。

垃圾回收。GC的存在極大的降低了併發程式碼的編寫,而且還提供了程式的健壯性。做為一個從C/C++做起,有過驅動開發經驗的程式設計師來說,GC這個東西是我一直沒有涉獵過的。對我來說GC就等於噩夢。但是當我開始試著接受GC時發現,GC真的是解決了程式設計師的生產力,極大的提高了效率。雖然目前來說GO已經1.4版本了,但是GC還算不得上優秀,按照GO的路線圖,後面會有更優秀的GC實現新增進來。

介面與struct。在第一次學習GO的interface的時候,我第一反應是這就是我想要的。雖然很多人也在說GO的這個interface的不好,而且說的很有道理,比如老趙的《為什麼我不喜歡GO語言式的介面》。interface可以通過組合擴充套件為新的interface,struct也可以通過組合擴充套件為新的struct。沒有繼承,只有組合。可以通過匿名組合達到類似繼承的效果。可以對interface進行查詢,有點類似COM的味道,但是語法層面上更為簡單。struct到interface的對映是隱式的,不需要宣告某個struct實現了某個interface。雖然可能會出現名字上的衝突,但是可以通過wrapper進行解決。這種interface的另外一個好處是單元測試時很容易實現MOCK,這點非常喜歡。也可以看這篇文章《Go interfaces make test stubbing easy

統一的工作佈局。GO定義了專案的目錄結構,比如bin目錄,pkg目錄以及src目錄。這個和我日常的專案佈局是一致的。之前用C++開發時我們也是如此安排佈局,所以就這點來說,我覺著容易過渡。

內建的併發原語。提到GO就不得不提到goroutine和channel。廉價的goroutine可以讓我們歡快的處理非同步任務,channel可以用來交換資料。藉助goroutine,可以很容易的實現高效能的服務端。goroutine及其排程器可以很容易和EPOLL,IOCP等系統機制結合起來,再通過Half Sync/Half Async模式,很容易在語法層面上達到同步形式,卻不失效能。

2013年初的時候還在做一個客戶端,當時使用了C++0x,其中印象最深的事情是lambad,std::function/bind,結合著執行緒+佇列的方式,可以很容易的實現類似於Chromuim的執行緒模型。在處理UI與慢任務比如讀檔案,請求網路資料等互動時,為了保證UI的體驗,使這些任務非同步化是非常有必要的。如果有對Golang瞭解或熟悉的朋友就會明白,這是類似與Golang的goroutine+channel。但是在使用這個模型時,覺著還是繁瑣了些,我要注意物件的生命週期,這個在C++雖然有各種智慧指標的幫助,但是難免還會掛一漏萬。而且遇到稍微的複雜的問題,比如一個慢任務接著一個慢任務時,就會涉及到一個任務鏈,在沒有future/promise(Facebook開源的folly庫中有個不錯的實現futures,後來還發現WINDOWS有個基於actor model的併發庫Asynchronous Agents Library非常不錯,只可惜目前只在WINDOWS上使用。)機制的幫助下很容易進入Callback Hell。這樣就會導致程式碼相對來說比較難維護,而且容易滋生BUG。好在當時這個部分的程式碼不是太多,而且也不是太過於複雜,很容易通過自測穩定下來。

上面雖然提到future/promise, AAL可以解決部分Callback Hell的問題,但是像future還是要用到callback。所以我在想,如果可以做到程式碼層面上是同步式的,背後卻是非同步的就爽了。GO就滿足了我這個需求。

GO標準庫中還提供了sync包,其中有基本的mutex說,還有RMutex這樣的讀寫鎖,還有Once,WaiterGroup等東西。基本滿足日常中對鎖的需求了。

GO為了幫助程式設計師解決在併發時經常遇到的race condition問題,還提供了相應的race condition工具。還有相應的死鎖檢測工具。

雖然GO社群有個slogan:”do not communicate by sharing memory; instead, share memory by communicating.“,但是每個goroutine之間並不是完全獨立的,一樣可以做到通過記憶體共享資料。這個時候只能依靠程式設計師自己去遵守了。而且因為goroutine不是完全獨立,panic這種東西就可能會導致整個程式掛掉。這點和Erlang比起來確實不是很好。

蛋疼的defer。用習慣了C++的RAII後,十分反感GO的defer機制,但是有的時候又不得不用。原因就是這個defer不是block級別的,而是函式級別的,需要在函式返回前才得到執行。所以這就會導致在處理一些類似於檔案開啟,操作再關閉的邏輯時非常蛋疼,回到了C的年代,必須手動去Close。

蛋疼的panic。雖然我在C++下不怎麼用異常,但是對於panic這個設計我表示非常的不滿意啊。因為它會影響全域性。而要捕捉panic就需要用defer。如果panic只是讓當前goroutine掛掉我覺著就嗨皮壞了。

沒有泛型。GO沒有泛型帶來的蛋疼地方是,要麼就用interface{}來做執行時泛型,要麼就自己手動寫程式碼生成器。比如我自己為了生成網路協議序列化程式碼就擼了一個生成器。而且因為沒有泛型,想實現類似C++ STL的容器與演算法基本沒太可能,當然方法還是有的,繼續使用程式碼生成器。而且GO1.4乾脆引入了一個叫go generate的命令。

總結

GO裡面其他一些內建的資料結構,比如slice,map等,但這些也是詬病,因為它又沒給予程式設計師可以享用range關鍵字的福利。

在GO的所有特性裡,最喜歡就是GC,goroutine,channel以及interface。而其餘的特性(比如上面我列舉的很多特性)我覺著都不是太重要,其中很多都可以在工程中實踐,和語言本身沒有太大關係。

總結下來,這東西就是一個工程工具,各種好用,但是從設計角度講各種粗糙,沒必要過度高估。它算的上工程實踐中的好朋友。在寫服務端時,它是把利器,至少在寫服務端程式時,我自己感覺如此。

有朋友說一個語言好不好就看它有沒有開拓你的眼界,給予你新的思想,我想至少GO在這點上滿足了。

文章推薦

Less is exponentially more

Go在谷歌:以軟體工程為目的的語言設計

Why Go is not Good

為什麼GO如此不受待見

Go的優缺點

Go at Cloudflare

Go at Soundcloud

相關文章