go入門實踐

0、和java一樣,go也是跨平臺,天生支持Unicode。但是go直接生成可執行文件,性能更高,內存佔用少。但是又和java一樣,go打出來的二進制包能夠掃描到依賴的庫,如果庫有漏洞,安全掃描會被掃出來。

1、安裝、環境配置及術語

從https://golang.google.cn/下載對應的版本。

使用命令行go env可以查看,如下:

[lightdb@lightdb-dev ~]$ go env
GO111MODULE=""   #設置是否打開go modules,auto/on/off三個取值,1.13開始默認,1.14開始推薦
GOARCH="amd64"
GOBIN=""    # go install最終拷貝到的目錄,一般go程序打成tar.gz分發,所以關係不大,配置的話指向$GOPATH/bin
GOCACHE="/home/lightdb/.cache/go-build"
GOENV="/home/lightdb/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/lightdb/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/lightdb/go"   # 項目的根目錄,最重要的環境變量
GOPRIVATE=""
GOPROXY="https://goproxy.cn,direct"   # GOPROXY是用來設置go mod的代理,以英文逗號“,”分割,使Go在後續拉取模塊版本時能夠脫離傳統的VCS方式從鏡像站點快速拉取。它擁有一個默認:https://proxy.golang.org,direct,但proxy.golang.org在中國無法訪問,所以建議使用goproxy.cn替代
GOROOT="/usr/lib/golang"    # golang的安裝目錄,跟JAVA_HOME一樣
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/lib/golang/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.20.6"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build261630818=/tmp/go-build -gno-record-gcc-switches"

hello world驗證環境及熟悉目錄

  [lightdb@lightdb-dev go]$ cd src/example/
  [lightdb@lightdb-dev example]$ go mod init

[lightdb@lightdb-dev go]$ go run src/example/first.go 
123 hello world 65 12.345
[lightdb@lightdb-dev go]$ pwd
/home/lightdb/go
[lightdb@lightdb-dev go]$ 
[lightdb@lightdb-dev bin]$ export | grep GO
declare -x GOBIN="//home/lightdb/go/bin"
declare -x GOPATH="/home/lightdb/go"
declare -x GOROOT="//home/lightdb/go/go"

 

 這樣腳手架就可以運行了。

[lightdb@lightdb-dev src]$ cd go-web/
[lightdb@lightdb-dev go-web]$ go build ginweb.go   # go build也是個很大的範疇,可以參考https://blog.csdn.net/wan212000/article/details/124288970

相關的報錯及原因和解決辦法如下:

[lightdb@lightdb-dev go]$ go build
go: go.mod file not found in current directory or any parent directory; see 'go help modules'
[lightdb@lightdb-dev go]$ go mod init   # 理論上go mod init即可
go: cannot determine module path for source directory /home/lightdb/go (outside GOPATH, module path must be specified)

Example usage:
    'go mod init example.com/m' to initialize a v0 or v1 module
    'go mod init example.com/m/v2' to initialize a v2 module

Run 'go help mod init' for more information.
[lightdb@lightdb-dev go]$ go mod init example.com/m   # 可以提前預建目錄,這樣cd dir; go mod init即可,不用指定名稱
go: creating new go.mod: module example.com/m
go: to add module requirements and sums:
    go mod tidy
[lightdb@lightdb-dev go]$ go build
$GOPATH/go.mod exists but should not    # 使用go module模塊化機制後,go.mod需要放在具體的模塊中而非在GOPATH目錄下,如$GOPATH/src/example/go.mod,再運行即可
[lightdb@lightdb-dev go]$ go install
$GOPATH/go.mod exists but should not
[lightdb@lightdb-dev go]$ go run src/go-web/ginweb.go 
src/go-web/ginweb.go:7:2: no required module provides package github.com/gin-gonic/gin: go.mod file not found in current directory or any parent directory; see 'go help modules'
src/go-web/ginweb.go:5:2: package tool/controller is not in GOROOT (/usr/lib/golang/src/tool/controller)    # 需要在模塊的根目錄下打包和運行

package first is not in GOROOT (/usr/lib/golang/src/first)   # 在$GOPATH目錄下執行  ln -s /usr/lib/golang go

1.1 主要文件

go.mod   Go Modules 的核心文件,包含module、require、replace 和 exclude 4部分。

go.sum   供 Go 命令在構建時判斷依賴包是否合法

詳見https://zhuanlan.zhihu.com/p/635696935,講得很清楚了。

1.2 依賴關係

和java一樣,go的依賴管理也經過了多次構造,最新爲2019年的go mode。

模塊是go管理依賴的基本單元,模塊由多個package組成。go.mod 文件定義了模塊的名稱及其依賴包,通過導入路徑和版本描述一個依賴。

[lightdb@lightdb-dev go]$ go help modules
Modules are how Go manages dependencies.

A module is a collection of packages that are released, versioned, and
distributed together. Modules may be downloaded directly from version control
repositories or from module proxy servers.

For a series of tutorials on modules, see
https://golang.org/doc/tutorial/create-module.

For a detailed reference on modules, see https://golang.org/ref/mod.

By default, the go command may download modules from https://proxy.golang.org.
It may authenticate modules using the checksum database at
https://sum.golang.org. Both services are operated by the Go team at Google.
The privacy policies for these services are available at
https://proxy.golang.org/privacy and https://sum.golang.org/privacy,
respectively.

The go command's download behavior may be configured using GOPROXY, GOSUMDB,
GOPRIVATE, and other environment variables. See 'go help environment'
and https://golang.org/ref/mod#private-module-privacy for more information.

 

2、IDE配置(vscode爲例)及調試

  vscode支持遠程開發和本地開發,attach/launch都一樣,都支持本地和遠程。所以除非要訪問linux資源,否則直接windows開發也可以,這和c/c++語言需要依賴很多posix兼容庫如pthread.h/uinstd.h/sys/select.h等具有很大的差別。

2.1 工程結構推薦

   一般來說,一個公司多個模塊,下面的結構就行。

GOPATH  #環境變量配置
	bin
	pkg
    src
        com.xxx    頂級域名作爲區分,便於和三方引入包保持一致
            lightdb
                main.go
                base
                service1
                service2
            unisql
                main.go
                base
                service1
                service2
            infra
                module1
                module2

   go以包爲組織單位,雖然分目錄,但包名和目錄名不一定要相同(只是習慣上最後一層相同),而且包是單層如packageName而非com.xxx.packageName。一個文件夾下的所有文件必須具有相同的包名聲明。

  import的是路徑名而非包名(因爲一個目錄下的所有文件需要聲明爲一個包,所以import目錄就相當於import包),引用的是GOPATH(跟CLASSPATH很像)開始的相對路徑。需要注意的是如果目錄裏面有子目錄,則不是同一個包,這個和java是一樣的。

  如果多個目錄下存在同名的package(這可是必不可免的),需要在import這些目錄時爲每個目錄指定一個package的別名。

2.2、代碼輔助、跳轉及格式化配置

2.3、debug和attach配置

 

3、基本概念

每個 Go 程序都是由包構成的。程序從 main 包開始運行。

一個程序中可以有很多main包,這個main包之間無法相互調用,運行時通過go run xxx.go運行。

 按照約定,包名與導入路徑的最後一個元素一致(但不強制)。例如,"math/rand" 包中的源碼均以 package rand 語句開始。

go裏面只有包和函數級別的變量,不存在對象級別。 也就是沒有對象。

go裏面,類型名在變量名之後,函數返回值在最後(這和java/c++相反),如下:

package main

import "fmt"

func add(x int, y int) int {   // 有點類似scala,和java/c都不一樣
    return x + y
}

func main() {
    fmt.Println(add(42, 13))
}

函數可以返回多值,這個和主流編程語言(如java/c/c++)存在明顯的差異,雖然c++在<utility>中支持返回pair帶兩個值

支持string、bool和無符號類型(和java/c的差別,java不支持無符號,c在17之前不支持布爾,一般模式typedef定義1和0來模擬),類型轉換需要強制寫,比如int到float,跟postgresql的強類型一樣。而且轉換是函數調用式的語法target_type(src_val)而不是java/c裏面的(target_type) src_val。如下:

package main

import (
    "fmt"
    "math"
)

func main() {
    var x, y int = 3, 4
    var f float64 = math.Sqrt(float64(x*x + y*y))  // float64是類型轉換
    var z uint = uint(f)
    fmt.Println(x, y, z)
}

for/if/else/switch更像是pl/sql的用法加上另外一半的c/c++/java,必須大括號、條件不必強制括號,switch不用帶break(因爲內置)。支持將某個語句延遲到函數調用(defer)返回後在執行(有點aop after的概念哈)

package main

import "fmt"

func main() {
    defer fmt.Println("world")

    fmt.Println("hello")
}

函數內外語句使用差異明顯,var和:=

package main

import "fmt"

func main() {
    var i, j int = 1, 2
    k := 3   // 函數外的每個語句都必須以關鍵字開始(var, func 等等),因此 := 結構不能在函數外使用。
    c, python, java := true, false, "no!"

    fmt.Println(i, j, k, c, python, java)
}

包成員可見性,大寫字母開頭爲默認導出,小寫則不導出,因爲大部分開發喜歡默認小寫標識符,所以其實對於控制可訪問性設計比java/c++可更妥些。

init()函數

和c/c++裏面dlopen()加載so文件後自動執行_init()函數一樣,init()函數在每次import一個包後自動執行。如果import相互依賴,也不會重複加載,每個包只會被初始化一次。

基本類型以外的高級數據類型

指針(雖然支持,但基本不推薦使用,除非爲了效率),go中非簡單類型也是傳值、並且沒有對象java的概念,沒有c++的傳引用,所以指針不可避免還是會用的多。

結構體(go不支持類,結構體上支持定義方法),結構體上的方法(按照go官方說法:方法只是個帶接收者參數的函數)語法略微怪異,如下:

package main

import (
    "fmt"
    "math"
)

type Vertex struct {
    X, Y float64
}

func (v Vertex) Abs() float64 {
    return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func main() {
    v := Vertex{3, 4}
    fmt.Println(v.Abs())
}

支持接口類型(也就是interface的概念)

type Abser interface {
    Abs() float64
}

可變長度數組(切片實現,也支持數組的數組)

map(原生支持)

支持函數作爲參數和返回值(像函數指針或c++裏面的function對象),此時函數支持閉包的概念(其實就是中間變量)。

上面兩點更像c with object

協程(goroutine和channel):併發的核心是:不用使用共享內存來通信,而是用通信(channel)來共享內存。

5、go自動化迴歸單元測試

默認情況下,go的單元測試文件放在和源代碼一樣的目錄下,運行go test會對所有的xxx_test.go進行測試,如下:

[lightdb@lightdb-dev go-web]$ go test
?       tool    [no test files]

這是一個比較大的主題,可以參考有贊paas的做法,https://cloud.tencent.com/developer/article/1684515,其中還結合了和sonar報告的集成

https://before80.github.io/go_docs_with_hugo/goBlog/2023/CodeCoverageForGoIntegrationTests/

https://go.dev/blog/integration-test-coverage  go 1.20在go build上增加了-cover選項,支持集成測試統計

對於集成測試,建議搭建一個專門的平臺維護用例輸出和輸入,自動化迴歸和對比,開發、測試一體化,跟postgresql的make check/make checkworld一樣。

java jacoco是一個可以單元測試和集成測試均覆蓋的覆蓋率工具,支持java agent模式統計。輸出也很完整。

https://blog.csdn.net/d_chunyu/article/details/117136293

6、makefile不僅可用於c/c++,還可以用於go。也就是makefile可用於任何linux下的開發,跟語言無關。尤其是go涉及到部分代碼使用c/c++開發時更是如此。

7、命令行程序設計

go提供了一個設計命令行程序的框架cobra cli(Kubernetes, Docker, Hugo,pgcenter均基於它)

8、go web開發之gin框架

9、go訪問數據庫之postgresql

10、模塊分發

go build可以用來打包整個模塊

go install將其安裝到$GOBIN目錄下

https://stackoverflow.com/questions/59741795/how-to-distribute-a-go-module-with-c-dependencies

11、三方庫

  日誌:logrus

  kafka:kafka-go ,https://blog.csdn.net/qq_30614345/article/details/131056586,https://zhuanlan.zhihu.com/p/431434480

  json:jsoniter

  orm:https://github.com/go-gorm/gorm

  sql解析:https://github.com/pingcap/parser,當然也可以用postgresql的c語言sql解析器(性能更強),pgpool-ii就是剝離的psotgresql解析器

Golang 程序員開發效率神器彙總!

查看某個模塊依賴的包清單

https://blog.csdn.net/aaqq123456654321/article/details/127113393

go性能分析

pgo,pprof

go與c/c++交互 cgo及環境變量

內存管理

gc

打印堆棧及性能

https://www.51cto.com/article/748837.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章