直接在終端中輸入 go help 便可顯示全部的 go 命令以及相應命令功能簡介,主要有下面這些:git
命令的使用方式爲: go command [args]
, 除此以外,可使用go help <command>
來顯示指定命令的更多幫助信息。github
在運行 go help 時,不只僅打印了這些命令的基本信息,還給出了一些概念的幫助信息:正則表達式
一樣使用 go help <topic>
來查看這些概念的的信息。瀏覽器
就像其餘靜態類型語言同樣,要執行 go 程序,須要先編譯,而後在執行產生的可執行文件。go build
命令就是用來編譯 go程序生成可執行文件的。但並非全部的 go 程序均可以編譯生成可執行文件的, 要生成可執行文件,go程序須要知足兩個條件:安全
也就是說go程序的入口就是 main.main
, 即main包下的main函數, 例子(hello.go):服務器
package main import "fmt" func main() { fmt.Println("Hello World!") }
編譯hello.go,而後運行可執行程序:網絡
$ go run hello.go # 將會生成可執行文件 hello $ ./hello # 運行可執行文件 Hello World!
go build+文件列表:數據結構
go build file1.go file2.go……
可編譯同目錄的多個源碼文件,go build 會編譯這些源碼,輸出可執行文件併發
go build 還有一些附加參數,能夠顯示更多的編譯信息和更多的操做,詳見下表所示。
附加參數 | 備 注 |
---|---|
-v | 編譯時顯示包名 |
-p n | 開啓併發編譯,默認狀況下該值爲 CPU 邏輯核數 |
-a | 強制從新構建 |
-n | 打印編譯時會用到的全部命令,但不真正執行 |
-x | 打印編譯時會用到的全部命令 |
-race | 開啓競態檢測 |
上面就是 go build 的基本用法,另外若是使用 go build 編譯的不是一個可執行程序,而是一個包,那麼將不會生成可執行文件。
而 go run
命令能夠將上面兩步併爲一步執行(不會產生中間文件)。
$ go run hello.go Hello World!
注意:go run
不會在運行目錄下生成任何文件,可執行文件被放在臨時文件中被執行,工做目錄被設置爲當前目錄。在 go run 的後部能夠添加參數,這部分參數會做爲代碼能夠接受的命令行輸入提供給程序。
go run 不能使用「go run+包」的方式進行編譯,如需快速編譯運行包,須要使用以下步驟來代替:
這個命令是用來移除當前源碼包和關聯源碼包裏面編譯生成的文件。這些文件包括
_obj/ 舊的object目錄,由Makefiles遺留 _test/ 舊的test目錄,由Makefiles遺留 _testmain.go 舊的gotest文件,由Makefiles遺留 test.out 舊的test記錄,由Makefiles遺留 build.out 舊的test記錄,由Makefiles遺留 *.[568ao] object文件,由Makefiles遺留 DIR(.exe) 由go build產生 DIR.test(.exe) 由go test -c產生 MAINFILE(.exe) 由go build MAINFILE.go產生 *.so 由 SWIG 產生
我通常都是利用這個命令清除編譯文件,而後github遞交源碼,在本機測試的時候這些編譯文件都是和系統相關的,可是對於源碼管理來講不必。
$ go clean -i -n cd /Users/astaxie/develop/gopath/src/mathapp rm -f mathapp mathapp.exe mathapp.test mathapp.test.exe app app.exe rm -f /Users/astaxie/develop/gopath/bin/mathapp
附加參數 | 備註 |
---|---|
-i | 清除關聯的安裝的包和可運行文件,也就是經過go install安裝的文件 |
-n | 把須要執行的清除命令打印出來,可是不執行,這樣就能夠很容易的知道底層是如何運行的 |
-r | 循環的清除在import中引入的包 |
-x | 打印出來執行的詳細命令,其實就是-n打印的執行版本 |
go 語言有一個褒貶不一的特性,就是對格式的要求很嚴格,我是很喜歡這個特性的,由於能夠保持代碼的清晰一致,編譯組合開發,而且go還提供了一個很是強大的工具來格式化代碼,它就是 go fmt sourcefile.go
, 不過一般其實不須要咱們手動調用,各類編輯器均可以幫助咱們自動完成格式化。
go doc
命令能夠方便咱們快速查看包文檔,go doc package
命令將會在終端中打印出指定 package 的文檔。
另外有一個與 go doc
命令相關的命令是 godoc
, 能夠經過它啓動咱們本身的文檔服務器:
godoc -http=:8080
而後咱們就可與在瀏覽器localhost:8080
中查看go文檔了
go get 能夠藉助代碼管理工具經過遠程拉取或更新代碼包及其依賴包,並自動完成編譯和安裝。整個過程就像安裝一個 App 同樣簡單。
使用 go get 前,須要安裝與遠程包匹配的代碼管理工具,如 Git、SVN、HG 等,參數中須要提供一個包名。
Go 語言的代碼被託管於 Github.com 網站,該網站是基於 Git 代碼管理工具的,不少有名的項目都在該網站託管代碼。其餘相似的託管網站還有 code.google.com、bitbucket.org 等。
這些網站的項目包路徑都有一個共同的標準,參見下圖所示
圖中的遠程包路徑是 Go 語言的源碼,這個路徑共由 3 個部分組成:
默認狀況下,go get 能夠直接使用。例如,想獲取 go 的源碼並編譯,使用下面的命令行便可:
$ go get github.com/davyxu/cellnet
獲取前,請確保 GOPATH 已經設置。Go 1.8 版本以後,GOPATH 默認在用戶目錄的 go 文件夾下。
cellnet 只是一個網絡庫,並無可執行文件,所以在 go get 操做成功後 GOPATH 下的 bin 目錄下不會有任何編譯好的二進制文件。
須要測試獲取並編譯二進制的,能夠嘗試下面的這個命令。當獲取完成後,就會自動在 GOPATH 的 bin 目錄下生成編譯好的二進制文件。
$ go get github.com/davyxu/tabtoy
使用 go get 時能夠配合附加參數顯示更多的信息及實現特殊的下載和安裝操做,詳見下表所示。
附加參數 | 附加參數 |
---|---|
-v | 顯示操做流程的日誌及信息,方便檢查錯誤 |
-u | 下載丟失的包,但不會更新已經存在的包 |
-d | 只下載,不安裝 |
-insecure | 容許使用不安全的 HTTP 方式進行下載操做 |
go install 的功能和 go build 相似,附加參數絕大多數均可以與 go build 通用。go install 只是將編譯的中間文件放在 GOPATH 的 pkg 目錄下,以及固定地將編譯結果放在 GOPATH 的 bin 目錄下。
go install 的編譯過程有以下規律:
Go 語言擁有一套單元測試和性能測試系統,僅須要添加不多的代碼就能夠快速測試一段需求代碼。
性能測試系統能夠給出代碼的性能數據,幫助測試者分析性能問題。
提示
單元測試(unit testing),是指對軟件中的最小可測試單元進行檢查和驗證。對於單元測試中單元的含義,通常要根據實際狀況去斷定其具體含義,如C語言中單元指一個函數,Java 裏單元指一個類,圖形化的軟件中能夠指一個窗口或一個菜單等。總的來講,單元就是人爲規定的最小的被測功能模塊。
單元測試是在軟件開發過程當中要進行的最低級別的測試活動,軟件的獨立單元將在與程序的其餘部分相隔離的狀況下進行測試。
要開始一個單元測試,須要準備一個 go 源碼文件,在命名文件時須要讓文件必須以_test
結尾。
單元測試源碼文件能夠由多個測試用例組成,每一個測試用例函數須要以Test爲前綴,例如:
func TestXXX( t *testing.T )
package code11_3 import "testing" func TestHelloWorld(t *testing.T) { t.Log("hello world") }
代碼說明以下:
單元測試使用 go test 命令啓動,例如:
$ go test helloworld_test.go ok command-line-arguments 0.003s $ go test -v helloworld_test.go === RUN TestHelloWorld --- PASS: TestHelloWorld (0.00s) helloworld_test.go:8: hello world PASS ok command-line-arguments 0.004s
代碼說明以下:
go test 指定文件時默認執行文件內的全部測試用例。可使用-run
參數選擇須要的測試用例單獨執行,參考下面的代碼。
package code11_3 import "testing" func TestA(t *testing.T) { t.Log("A") } func TestAK(t *testing.T) { t.Log("AK") } func TestB(t *testing.T) { t.Log("B") } func TestC(t *testing.T) { t.Log("C") }
這裏指定 TestA 進行測試:
$ go test -v -run TestA select_test.go === RUN TestA --- PASS: TestA (0.00s) select_test.go:6: A === RUN TestAK --- PASS: TestAK (0.00s) select_test.go:10: AK PASS ok command-line-arguments 0.003s
TestA 和 TestAK 的測試用例都被執行,緣由是-run跟隨的測試用例的名稱支持正則表達式,使用-run TestA$便可只執行 TestA 測試用例。
當須要終止當前測試用例時,可使用 FailNow,參考下面的代碼。
func TestFailNow(t *testing.T) { t.FailNow() }
還有一種只標記錯誤不終止測試的方法,代碼以下:
func TestFail(t *testing.T) { fmt.Println("before fail") t.Fail() fmt.Println("after fail") }
測試結果以下:
=== RUN TestFail before fail after fail --- FAIL: TestFail (0.00s) FAIL exit status 1 FAIL command-line-arguments 0.002s
從日誌中看出,第 5 行調用 Fail() 後測試結果標記爲失敗,可是第 7 行依然被程序執行了。
每一個測試用例可能併發執行,使用 testing.T 提供的日誌輸出能夠保證日誌跟隨這個測試上下文一塊兒打印輸出。testing.T 提供了幾種日誌輸出方法,詳見下表所示。
單元測試框架提供的日誌方法
方 法 | 備 注
---|---
Log | 打印日誌,同時結束測試
Logf | 格式化打印日誌,同時結束測試
Error | 打印錯誤日誌,同時結束測試
Errorf | 格式化打印錯誤日誌,同時結束測試
Fatal | 打印致命日誌,同時結束測試
Fatalf | 格式化打印致命日誌,同時結束測試
開發者能夠根據實際須要選擇合適的日誌。
基準測試能夠測試一段程序的運行性能及耗費 CPU 的程度。Go 語言中提供了基準測試框架,使用方法相似於單元測試,使用者無須準備高精度的計時器和各類分析工具,基準測試自己便可以打印出很是標準的測試報告。
下面經過一個例子來了解基準測試的基本使用方法。
package code11_3 import "testing" func Benchmark_Add(b *testing.B) { var n int for i := 0; i < b.N; i++ { n++ } }
這段代碼使用基準測試框架測試加法性能。第 7 行中的 b.N 由基準測試框架提供。測試代碼須要保證函數可重入性及無狀態,也就是說,測試代碼不使用全局變量等帶有記憶性質的數據結構。避免屢次運行同一段代碼時的環境不一致,不能假設 N 值範圍。
使用以下命令行開啓基準測試:
$ go test -v -bench=. benchmark_test.go goos: linux goarch: amd64 Benchmark_Add-4 20000000 0.33 ns/op PASS ok command-line-arguments 0.700s
代碼說明以下:
-bench=.
表示運行 benchmark_test.go 文件裏的全部基準測試,和單元測試中的-run相似。注意:Windows 下使用 go test 命令行時,-bench=.
應寫爲-bench="."
。
基準測試框架對一個測試用例的默認測試時間是 1 秒。開始測試時,當以 Benchmark 開頭的基準測試用例函數返回時還不到 1 秒,那麼 testing.B 中的 N 值將按 一、二、五、十、20、50……遞增,同時以遞增後的值從新調用基準測試用例函數。
經過-benchtime
參數能夠自定義測試時間,例如:
$ go test -v -bench=. -benchtime=5s benchmark_test.go goos: linux goarch: amd64 Benchmark_Add-4 10000000000 0.33 ns/op PASS ok command-line-arguments 3.380s
基準測試能夠對一段代碼可能存在的內存分配進行統計,下面是一段使用字符串格式化的函數,內部會進行一些分配操做。
func Benchmark_Alloc(b *testing.B) { for i := 0; i < b.N; i++ { fmt.Sprintf("%d", i) } }
在命令行中添加-benchmem
參數以顯示內存分配狀況,參見下面的指令:
$ go test -v -bench=Alloc -benchmem benchmark_test.go goos: linux goarch: amd64 Benchmark_Alloc-4 20000000 109 ns/op 16 B/op 2 allocs/op PASS ok command-line-arguments 2.311s
代碼說明以下:
開發者根據這些信息能夠迅速找到可能的分配點,進行優化和調整
有些測試須要必定的啓動和初始化時間,若是從 Benchmark() 函數開始計時會很大程度上影響測試結果的精準性。testing.B 提供了一系列的方法能夠方便地控制計時器,從而讓計時器只在須要的區間進行測試。咱們經過下面的代碼來了解計時器的控制。
func Benchmark_Add_TimerControl(b *testing.B) { // 重置計時器 b.ResetTimer() // 中止計時器 b.StopTimer() // 開始計時器 b.StartTimer() var n int for i := 0; i < b.N; i++ { n++ } }
從 Benchmark() 函數開始,Timer 就開始計數。StopTimer() 能夠中止這個計數過程,作一些耗時的操做,經過 StartTimer() 從新開始計時。ResetTimer() 能夠重置計數器的數據。
計數器內部不只包含耗時數據,還包括內存分配的數據。
go test package //生成並運行測試該源碼包下面全部_test文件下全部測試方法直到測試完畢 go test *_test.go //生成並運行測試該文件下全部測試方法直到測試完畢 go test -v *_test.go //測試該文件,並顯示測試的詳細命令 go test -v -bench=. *_test.go //執行相應的benchmarks方法,例如 -bench=.執行全部 go test -cover *_test.go //開啓測試覆蓋率 go test -run regexp *_test.go //正則匹配,例如 -run=Array 那麼就執行包含有Array開頭的函數 go test -v -run Test_A *_test.go //自定義測試方法 go test -v -bench=. -benchtime=5s *_test.go //自定義測試時間 go test -v -bench=Alloc -benchmem b*_test.go //測試內存,本例爲對指定方法進行測試並測試內存 go test -cpuprofile cpu.out *_test.go //輸出cpu性能分析文件 go test -memprofile mem.out *_test.go //輸出內存性能分析文件 go test -blockprofile block.out *_test.go //輸出內部goroutine阻塞的性能分析文件
Go 語言工具鏈中的 go pprof 能夠幫助開發者快速分析及定位各類性能問題,如 CPU 消耗、內存分配及阻塞分析。
性能分析首先須要使用 runtime.pprof 包嵌入到待分析程序的入口和結束處。runtime.pprof 包在運行時對程序進行每秒 100 次的採樣,最少採樣 1 秒。而後將生成的數據輸出,讓開發者寫入文件或者其餘媒介上進行分析。
go pprof 工具鏈配合 Graphviz 圖形化工具能夠將 runtime.pprof 包生成的數據轉換爲 PDF 格式,以圖片的方式展現程序的性能分析結果
Graphviz 是一套經過文本描述的方法生成圖形的工具包。描述文本的語言叫作 DOT。
在 www.graphviz.org(http://www.graphviz.org)網站能夠獲取到最新的 Graphviz 各平臺的安裝包。
CentOS 下,可使用 yum 指令直接安裝:
$ yum install graphiviz
runtime.pprof 提供基礎的運行時分析的驅動,可是這套接口使用起來還不是太方便,例如:
不少第三方的包在系統包 runtime.pprof 的技術上進行便利性封裝,讓整個測試過程更爲方便。這裏使用 github.com/pkg/profile 包進行例子展現,使用下面代碼安裝這個包:
$ go get github.com/pkg/profile
下面代碼故意製造了一個性能問題,同時使用 github.com/pkg/profile 包進行性能分析。
package main import ( "github.com/pkg/profile" "time" ) func joinSlice() []string { var arr []string for i := 0; i < 100000; i++ { // 故意形成屢次的切片添加(append)操做, 因爲每次操做可能會有內存從新分配和移動, 性能較低 arr = append(arr, "arr") } return arr } func main() { // 開始性能分析, 返回一箇中止接口 stopper := profile.Start(profile.CPUProfile, profile.ProfilePath(".")) // 在main()結束時中止性能分析 defer stopper.Stop() // 分析的核心邏輯 joinSlice() // 讓程序至少運行1秒 time.Sleep(time.Second) }
代碼說明以下:
性能分析須要可執行配合才能生成分析結果,所以使用命令行對程序進行編譯,代碼以下:
$ go run cpu.go $ go tool pprof --pdf cpu.pprof > cpu.pdf
代碼說明以下:
第 1 行運行 cpu.go ,在當前目錄輸出 cpu.pprof 文件。
第 2 行,使用 go tool 工具鏈輸入 cpu.pprof ,生成 PDF 格式的輸出文件,將輸出文件重定向爲 cpu.pdf 文件。這個過程當中會調用 Graphviz 工具,Windows 下需將 Graphviz 的可執行目錄添加到環境變量 PATH 中。
最終生成 cpu.pdf 文件,使用 PDF 查看器打開文件,觀察後發現下圖所示的某個地方可能存在瓶頸。
func joinSlice() []string { const count = 100000 var arr []string = make([]string, count) for i := 0; i < count; i++ { arr[i] = "arr" } return arr }
代碼說明以下:
其餘命令不會常用,這裏就不介紹了,真的用到的時候,直接使用 go help command
便可查看相關命令。