以下為學習本課程 Pluralshight: The Go CLI Playbook 所記錄的筆記,了解Golang本身所提供的指令集工具要如何使用
https://app.pluralsight.com/course-player?clipId=38b3a654-cbfd-436c-acb1-df088dfb5a48
Go Command 功能
Go 的基本Command可以提供以下功能
- Building Application: 把
.go
檔案編譯成執行檔 - Testing Application: 測試
.go
程式 - Profiling Application: 效能測試
- Managing Workspaces: 管理開發環境
- Interacting with Environment: 與環境互動
Go Command的列表
在終端機下 go
指令,就會顯示go command的詳細資訊
1 | ➜ go |
可透過 go help
可看到特定command的指令
假如要看go test的介紹,那就下這行指令 go help test
1 | ➜ go help test |
go env
查看環境變數
1 | > go env |
go run
執行程式
注意,若要運程
go run
,需要在$GOPATH
底下的src
目錄進行
運行一個包含main()方法的程式
1 | > go run main.go |
透過 go run --race
來偵測是否會有race condition的狀況發生
若目前有個使用gorouting
的程式,若要偵測是否會發生race condition狀況,可以加上-race
這個flag
1 | package main |
若有race condition,go tool會產出報告來解釋
1 | ➜ go run -race race_condition/main.go |
1 | 一般來說在dev模式時,會建議每次運行專案時,都加上-race |
go build
編譯程式與其依賴
go build
會把專案編譯成執行檔
若目前有個在cmds資料夾內的專案叫做 hello
並引用了helloService
這個依賴內的方法
cmds/hello.go
1 | package main |
helloService/msg.go
1 | package hello |
那在 /cmds/hello
專案中執行 go build
,會在當前目錄產生hello
執行檔
可直接執行該執行檔
使用 go build -o
定義編譯後檔案名稱
假如要把編譯檔案叫做hello_exe.a
1 | ➜ go build -o hello_exe.a |
使用 go build -i
編譯專案的依賴
若對專案進行編譯時,加上-i
參數,會同時將import的依賴 (ex:helloService
)也會編譯該依賴們並安裝在 $GOPATH/pkg
底下
參考
Go Official Docs: Command go
https://golang.org/cmd/go/#Compile packages and dependencies
go install
編譯專案成為package
會將專案 (main) 進行編譯成bin檔案並放置在$GOPATH/bin
底下
會將依賴 (package) 進行編譯並安裝在$GOPATH/pkg
底下
1 | 如果已經有存在的依賴或是binary檔案、那go install就不會有作用 |
go install -n
:顯示會進行的動作,但不會真正執行
假如我們在對剛剛的helloService這個依賴,進行go install -n
會看到不會做什麼動作,因為已經有安裝該依賴在pkg
底下
將該依賴移除,再次執行一遍,會看到go tool執行了什麼動作
go install -x
:顯示會進行的動作,但會真正執行
其他flag
- -p n: 指定用多少個cpu來進行install, 預設是可取得的數量
產生Shared Libraries,使用-buildmode
flag
透過 go build -buildmode
或是 go install -buildmode
可以像C或C++那樣產生Shared Libraries,供動態載入
透過go help buildmode
可查看有提供哪些模式
1 | 預設-buildmode會使用 archive:將檔案編譯成 .a |
假如再用剛剛的範例,一個cmds/hello.go
中引用了 helloService這個package
使用 -buildmode=shared
產生供go動態連結的Shared Libraries
若要將某個package編譯成shared library,
舉個例子,把helloService
編譯成shared library並安裝在pkg
資料夾中
1 | ➜ go install -buildmode=shared helloService |
注意: Macos不支援 -buildmode=shared
-buildmode=shared not supported on darwin/amd64
接著編譯主程式,並指定其執行檔可以動態載入(-linkshared) shared library
1 | ➜ go build -linkshared cmds/hello |
執行編譯後的執行檔 ./hello
,可看到其結果
不過若將pkg
中,剛剛go install的helloService
的shared library給刪除的話,再次執行 /.hello
那就會發生無法執行狀況。
使用 -buildmode=c-archive
產生供C language "靜態"連結的Shared Libraries
如果想要將pakcage編譯成C可以載入的shared library的話,需要對pakcage的method,加上 //export functionName
1 | package helloService |
若使用 -buildmode=c-archive
的話,則會產生出 .h
與.a
的shared library
注意: Macos運行
-buildmode=c-archive
會無法產生.h
檔案 go版本: go1.15 darwin/amd64
接著就可以在C語言內引入用Go build好的share library
1 |
|
執行之
1 | cc hello.c ./hello |
使用 -buildmode=c-shared
產生供C language "動態"連結的Shared Libraries
同用go產生出shared library
使用 -buildmode=plugin
將所有import的packages,打包成go plugin
可以把專案需要import的package,打包成plugin
舉個例子,若現在我們要將plugin這一個pakcage打包成plugin
1 | package main |
透過以下指令,做出一個plugin,檔名叫做 plugin 副檔名為.so
1 | ➜ go build -buildmode=plugin -o=plugin.so plugin/plugin.go |
接著我們定義了一個小專案,來使用plugin
1 | package main |
接著運行下面指令使用plugin
1 | ➜ go run action/action.go -plugin=./plugin.so |
如果plugin存在的話,就可以執行 plugin.so
內的ThingToDo
方法
執行結果
參考
Golang的构建模式 - Chen Jiehua
https://chenjiehua.me/golang/golang-buildmode.html
go test
撰寫單元測試Unit Testing Programs
透過 go test
可測試golang撰寫的程式的Input與Output是否正確
測試需注意的規則
由於go本身對測試有著嚴謹的規則,故可以避免像其他語言那樣有各種不同測試的框架,以統一測試的樣式
其規則如下:
- 若有定義測試的檔案名稱,需要以
_test.go
最結尾命名
1 | mylib_test.go |
- 若需要被測試的方法,請以
Test_
為開頭
1 | func Test_MyFunction() {} |
執行測試
若有個專案叫做 testing_example
,其目錄架構如下
testing_example/mylib_test.go
1 | package mylib |
其子package也有定義testing file ``testing_example/childlib/childlib_test.go`
1 | package childlib |
執行測試的範疇
若要對專案執行Unit Test:
1 | ~/go/src via v1.15 |
但會發現child package的測試沒有執行到,所以若要對package內所有子
package都進行測試,那需要加上 /...
做遞迴尋找的動作
1 | ~/go/src via 🐹 v1.15 |
測試可以夾帶的參數 flags
透過 go help testflag
可以查看執行測試時可以客製化的選擇
列舉一些常使用的:
-cpu
: 指定測試時使用的CPU數量,預設是會使用GOMAXPROCS
參數的值-parallel
: 測試時可以並行處理,但只有指定Function才會被並行處理
1 | func Test_BasicChecks(t *testing.T) { |
-list
: 可透過正則表達式過濾出測試方法名稱,再測試時會列出符合的測試方法給
假如要列出有包含 Basics
的測試方法名稱:
1 | ~/go/src via v1.15 |
-run
: 跟List很像,但只會執行符合正則表達式的測試方法- 使用時機:若開發出子專案的測試方法,透過
-run
指定該子專案的scope執行測試就好,就可以省去不少時間
- 使用時機:若開發出子專案的測試方法,透過
1 | ~/go/src via v1.15 |
-timeout d
: 超過多久就停止測試,預設為10分鐘-v
: 測試時將詳細結果列出來
1 | ~/go/src via v1.15 |
-count
: 指定執行測試的次數-cover
: 顯示測試的涵蓋範圍資訊
測試的涵蓋 Code Coverage
可以透過 go test -cover
來查看目前測試項目涵蓋了多少開發的內容
以下為測試的項目內容:
mylib.go
1 | package mylib |
mylib_test.go
1 | package mylib |
可以看到上述的範例只測試一個方法
1 | ~/go/src via v1.15 |
透過go test -coverpkg
指定測試程式引用的package
再用上述的範例,我們打算測試專案 testing_example_1
中的 fmt
與 testing_example_1
本身自己這兩個pacakge:
1 | ~/go/src via v1.15 |
透過go test -coverprofile
產出測試的報告
1 | ~/go/src via v1.15 |
同時會產生 cover.out
這個測試報告檔案,內容如下
1 | mode: set |
透過 go tool 查看測試報告
可以透過 go tool cover -html= 測試報告檔案名稱
來產生出以網頁方式瀏覽的報告
若要解析產生出來的報告 cover.out
go tool cover -html=cover.out
可以產出Web呈現,測試的報告
如果要看出更詳細的測試報告的話,例如想看到測試方法被執行幾次
1 | ~/go/src via v1.15 |
一樣會產生出 cover.out
檔案,這時再透過 go tool cover -html
進行壓力測試 Benchmark Testing
golang的test工具亦提供benchmark的測試,可以看出該方法需要耗費多少CPU資源
規則
需要定義方法名稱前綴為 Benchmark
例如:
1 | func BenchmarkAdder(b *testing.B) { |
指令: go test -bench
若要對testing_example_2這個專案下的檔案:
mylib.go
1 | package mylib |
mylib_test.go
1 | package mylib |
這時透過 go test -bench Adder testing_example_2
,對testing_example_2
這專案內有包含 Adder
的壓測方法進行壓力測試
1 | ~/go/src via v1.15 |
BenchmarkAdder 這隻方法執行了1000000000次, 每次花了 0.374 ns
或是也可以不指定方法Benchmark名稱 go test -bench . testing_example_2
指定要花多少時間做benchmark測試: go test -benchtime
可指定壓測測多少時間,可以擴展壓測的次數
舉例,只壓測0.001秒,那可看到只會測出 2864049次
1 | ~/go/src via v1.15 |
1 |
|
~/go/src via v1.15
➜ go test -bench . -benchmem testing_example_2
goos: darwin
goarch: amd64
pkg: testing_example_2
BenchmarkAdder-4 1000000000 0.501 ns/op 0 B/op 0 allocs/op
PASS
ok testing_example_2 0.586s
1 |
|
sudo apt-get install graphyiz
1 |
|
mylib_test.go
1 | package mylib |
透過 go test -memprofile mem.out testing_example_2
來產出叫做 mem.out
的測試報告
測試結果
1 | ~/go/src via v1.15 |
以及會產生兩個檔案 mem.out
與 testing_example_2.test
,後者主要提供給 go tool pprof檢視
接著透過 go tool pprof
檢視其報告 testing_example_2.test
,並指定用-web
網頁的方式瀏覽
1 | sudo go tool pprof -web testing_example_2.test mem.out |
不過由於範例檔案規模太小,go tool無法捕捉到有用的資訊,所以抓不到
這時可以透過 go test -mmprofilerate n
來改變讀取報告的頻率,當執行n個指令時,就產出報告
接著再查看一次產出的報告
1 | sudo go tool pprof -web testing_example_2.test mem.out |
可以看到每個方法詳細的記憶體消耗資訊圖
產生CPU消費報告 go test -cpuprofile
一樣可以產生每個方法或package使用多少的CPU關聯圖
1 | ~/go/src via v1.15 |
產生出cpu.out
與testing_example_2.test
,然後後者也是透過 pprof
檢視
不過也是一樣沒看到豐富的測試資訊,因為整個測試只有執行一遍,導致pprof無法抓到有用的資訊
這時可以透過 -count
可以測試多次以取得足夠的CPU使用量報告
go test -cpuprofile cpu.out -count 1000000 testing_example_2
在查看一遍就會有方法呼叫的CPU消耗流程圖可以檢視了
產生程式足跡報告 go test -trace
透過-trace
這個flag, 可以產出許多檢視程式的執行報告
一樣透過 go test -trace trace.out testing_example_2
來產生叫做 trace.out
的報告
以 go tool trace trace.out
檢視內容
點開View trace可看到如下