一奔脐、熟悉Go語(yǔ)言運(yùn)行環(huán)境躯舔、包管理工具(手動(dòng)裝一下Go環(huán)境并從零運(yùn)行一個(gè)依賴(lài)第三方庫(kù)的Go程序)
go modules是官方提倡的新的包管理彭雾,乃至項(xiàng)目管理機(jī)制,可以不再需要GOPATH的存在串稀。
1除抛、go module 的初始化
golang提供了一個(gè) 環(huán)境變量“GO111MODULE”,默認(rèn)值為auto母截,如果當(dāng)前目錄里有g(shù)o.mod文件到忽,就使用 go modules, 否則就使用舊的GOPATH和vendor機(jī)制,因?yàn)樵趍odules機(jī)制下 go get 只會(huì)下載 go modules清寇,這一行為會(huì)在以后版本中成為默認(rèn)值喘漏,這里我們保持auto即可,如果你想直接使用modules而不需要從GOPATH過(guò)度华烟,那么把“GO111MODULE”設(shè)置為 on 翩迈。
modules和傳統(tǒng)的GOPATH不同,不需要包含例如src垦江,bin這樣的子目錄帽馋,一個(gè)源代碼目錄甚至是空目錄都可以作為module,只要其中包含有g(shù)o.mod文件比吭。
在新建的文件夾中寫(xiě)好依賴(lài)了第三方包的go程序绽族,即 main.go
在該文件夾下 通過(guò) ls 命令,可以查看到 目前只有 main.go 文件
(1)初始化modules衩藤,需要用到如下命令(前提是已經(jīng)安裝配置好golang1.11)
go mod init {module name}
我們的module叫test吧慢,所以
go mod init test
初始化完成后會(huì)在目錄下生成一個(gè) go.mod 文件,里面內(nèi)容只有一行“ module test”赏表。
(2)包管理 當(dāng)我們使用 go build检诗,go test 和 go list 時(shí),go 會(huì)自動(dòng)更新 go.mod 文件瓢剿,將依賴(lài)關(guān)系寫(xiě)入其中逢慌。
使用如下命令會(huì)自動(dòng)更新依賴(lài)關(guān)系,并將包下載放入 cache间狂。
go mod tidy
運(yùn)行完這個(gè)命令后攻泼,文件夾中會(huì)多一個(gè) go.sum 文件。
go.sum文件中主要是我們直接引用的package和它自身需要所依賴(lài)的版本記錄鉴象,go modules 就是根據(jù)這些去找到需要的packages的忙菠。
btw,如果我們不做任何修改纺弊,默認(rèn)會(huì)使用最新的包版本牛欢,如果包打過(guò)tag,那么就會(huì)使用最新的那個(gè)tag對(duì)應(yīng)的版本淆游。
**(3)使用 go build 來(lái)編譯我們的代碼
go build -mod=readonly
在這個(gè)模式下任何會(huì)導(dǎo)致依賴(lài)關(guān)系變動(dòng)的情況都將導(dǎo)致build失敗傍睹,前面提到過(guò)build能查找并更新依賴(lài)關(guān)系隔盛,使用這個(gè)選項(xiàng)可以檢查依賴(lài)關(guān)系的變動(dòng)。
同時(shí)焰望,當(dāng)運(yùn)行完這個(gè)命令以后骚亿,文件夾中會(huì)生成一個(gè) test 的執(zhí)行文件,
至此熊赖,main.go 的代碼已經(jīng)成功完成構(gòu)建来屠,包管理都由 go modules 替我們完成了。
btw震鹉,go build -mod=vendor
的意思是忽略cache里的包俱笛,只使用vendor目錄里面的版本。
2传趾、包的版本控制
包管理的另外一項(xiàng)重要功能就是包的版本控制
迎膜。modules同樣可以做到。
btw浆兰,在介紹版本控制之前磕仅,我們要先明確一點(diǎn),如果上層目錄和下層目錄的go.mod里有相同的package規(guī)則簸呈,那么上層目錄的無(wú)條件覆蓋下層目錄榕订,目的是為了main module的構(gòu)建不會(huì)被依賴(lài)的package所影響。
go.mod 文件內(nèi)容如下:
module test
require github.com/chromedp/chromedp v0.1.2
前面部分是包的名字蜕便,也就是import時(shí)需要寫(xiě)的部分劫恒,而空格之后的是版本號(hào),版本號(hào)遵循如下規(guī)律:
vX.Y.Z-pre.0.yyyymmddhhmmss-abcdefabcdef
vX.0.0-yyyymmddhhmmss-abcdefabcdef
vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdefabcdef
vX.Y.Z
即 版本號(hào)+時(shí)間戳+hash
我們自己指定版本時(shí)只需要制定版本號(hào)即可轿腺,沒(méi)有版本tag的則需要找到對(duì)應(yīng)commit的時(shí)間和hash值两嘴。
默認(rèn)使用最新版本的package。
如果我們需要修改包的版本號(hào)(比如想使用chromedp 的v0.1.0版本)
1族壳、只需要如下命令:
go mod edit -require="github.com/chromedp/chromedp@v0.1.0"
@后面加上你需要的版本號(hào)憔辫。go.mod已經(jīng)修改了:
module test
require github.com/chromedp/chromedp v0.1.0
2、還需要讓go modules 更新依賴(lài)仿荆,這里我們手動(dòng)執(zhí)行go mod tidy
命令螺垢,即可切換到v0.1.0版本。
[golang包管理解決之道——go modules初探
再探go modules:使用與細(xì)節(jié)
用 golang 1.11 module 做項(xiàng)目版本管理
二赖歌、熟悉Go語(yǔ)言以下內(nèi)容:
#### 1) 數(shù)據(jù)類(lèi)型(string、slice功茴、map)
Go 語(yǔ)言有 3 種數(shù)據(jù)結(jié)構(gòu)可以讓用戶(hù)管理集合數(shù)據(jù):數(shù)組庐冯、切片和映射。了解這些數(shù)據(jù)結(jié)構(gòu)坎穿,一般會(huì)從數(shù)組開(kāi)始展父,因?yàn)閿?shù)組是切片和映射的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)返劲。數(shù)組存儲(chǔ)的類(lèi)型可以是內(nèi)置類(lèi)型,如整型或者字符串栖茉,也可以是某種結(jié)構(gòu)類(lèi)型篮绿。
##### 數(shù)組
如果使用...替代數(shù)組的長(zhǎng)度, Go 語(yǔ)言會(huì)根據(jù)初始化時(shí)數(shù)組元素的數(shù)量來(lái)確定該數(shù)組的長(zhǎng)度吕漂。
// 聲明一個(gè)整型數(shù)組
// 用具體值初始化每個(gè)元素// 容量由初始化值的數(shù)量決定
array := [...]int{10, 20, 30, 40, 50}
二維數(shù)組
// 聲明一個(gè)二維整型數(shù)組亲配,兩個(gè)維度分別存儲(chǔ) 4 個(gè)元素和 2 個(gè)元素
var array [4][2]int
// 使用數(shù)組字面量來(lái)聲明并初始化一個(gè)二維整型數(shù)組
array := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
// 聲明并初始化外層數(shù)組中索引為 1 個(gè)和 3 的元素
array := [4][2]int{1: {20, 21}, 3: {40, 41}}
// 聲明并初始化外層數(shù)組和內(nèi)層數(shù)組的單個(gè)元素
array := [4][2]int{1: {0: 20}, 3: {1: 41}}
只要類(lèi)型一致,就可以將多維數(shù)組互相賦值惶凝,
// 將 array1 的索引為 1 的維度復(fù)制到一個(gè)同類(lèi)型的新數(shù)組里
var array3 [2]int = array1[1]
// 將外層數(shù)組的索引為 1吼虎、內(nèi)層數(shù)組的索引為 0 的整型值復(fù)制到新的整型變量里
var value int = array1[1][0]
使用指針在函數(shù)間傳遞大數(shù)組
//使用值傳遞,在函數(shù)間傳遞大數(shù)組
//聲明一個(gè)需要 8 MB 的數(shù)組
var array [1e6]int
// 將數(shù)組傳遞給函數(shù) foo
foo(array)// 函數(shù) foo 接受一個(gè) 100 萬(wàn)個(gè)整型值的數(shù)組
func foo(array [1e6]int) {
...
}
//使用指針在函數(shù)間傳遞大數(shù)組
// 分配一個(gè)需要 8 MB 的數(shù)組
var array [1e6]int
// 將數(shù)組的地址傳遞給函數(shù) foo
foo(&array)
// 函數(shù) foo 接受一個(gè)指向 100 萬(wàn)個(gè)整型值的數(shù)組的指針
func foo(array *[1e6]int) {
...
}
##### 切片
切片是圍繞動(dòng)態(tài)數(shù)組的概念構(gòu)建的苍鲜,可以按需自動(dòng)增長(zhǎng)和縮小思灰。切片的動(dòng)態(tài)增長(zhǎng)是通過(guò)內(nèi)置函數(shù) append
來(lái)實(shí)現(xiàn)的。這個(gè)函數(shù)可以快速且高效地增長(zhǎng)切片混滔。還可以通過(guò)對(duì)切片再次切片來(lái)縮小一個(gè)切片的大小洒疚。因?yàn)榍衅牡讓觾?nèi)存也是在連續(xù)塊中分配的,所以切片還能獲得索引坯屿、迭代以及為垃圾回收優(yōu)化的好處油湖。
內(nèi)部實(shí)現(xiàn)
切片有3個(gè)字段的數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)包含 Go 語(yǔ)言需要操作底層數(shù)組的元數(shù)據(jù)愿伴。這 3 個(gè)字段分別是指向底層數(shù)組的指針肺魁、切片訪(fǎng)問(wèn)的元素的個(gè)數(shù)(即長(zhǎng)度)和切片允許增長(zhǎng)到的元素個(gè)數(shù)(即容量)。
是否能提前知道切片需要的容量通常會(huì)決定要如何創(chuàng)建切片隔节。
方法一:使用內(nèi)置的make函數(shù)
- 使用長(zhǎng)度聲明一個(gè)字符串切片
// 創(chuàng)建一個(gè)字符串切片
// 其長(zhǎng)度和容量都是 5 個(gè)元素
slice := make([]string, 5)
//如果只指定長(zhǎng)度鹅经,那么切片的容量和長(zhǎng)度相等。
2)使用長(zhǎng)度和容量聲明整型切片
// 創(chuàng)建一個(gè)整型切片
// 其長(zhǎng)度為 3 個(gè)元素怎诫,容量為 5 個(gè)元素
slice := make([]int, 3, 5)
方法二:通過(guò)切片字面量來(lái)聲明切片
// 創(chuàng)建字符串切片
// 其長(zhǎng)度和容量都是 5 個(gè)元素
slice := []string{"Red", "Blue", "Green", "Yellow", "Pink"}
// 創(chuàng)建一個(gè)整型切片
// 其長(zhǎng)度和容量都是 3 個(gè)元素
slice := []int{10, 20, 30}
使用索引聲明切片
// 創(chuàng)建字符串切片
// 使用空字符串初始化第 100 個(gè)元素
slice := []string{99: ""}
如果在[]運(yùn)算符里指定了一個(gè)值瘾晃,那么創(chuàng)建的就是數(shù)組而不是切片。只有不指定值
的時(shí)候幻妓,才會(huì)創(chuàng)建切片.
創(chuàng)建nil切片
空切片在底層數(shù)組包含 0 個(gè)元素蹦误,也沒(méi)有分配任何存儲(chǔ)空間。想表示空集合時(shí)空切片很有用肉津,例如强胰,數(shù)據(jù)庫(kù)查詢(xún)返回 0 個(gè)查詢(xún)結(jié)果時(shí)
// 聲明空切片
// 使用 make 創(chuàng)建空的整型切片
slice := make([]int, 0)
// 使用切片字面量創(chuàng)建空的整型切片
slice := []int{}
使用切片創(chuàng)建切片
// 創(chuàng)建一個(gè)整型切片
// 其長(zhǎng)度和容量都是 5 個(gè)元素
slice := []int{10, 20, 30, 40, 50}
// 創(chuàng)建一個(gè)新切片
// 其長(zhǎng)度為 2 個(gè)元素,容量為 4 個(gè)元素
newSlice := slice[1:3]
對(duì)底層數(shù)組容量是 k 的切片 slice[i:j]來(lái)說(shuō)
長(zhǎng)度: j - i
容量: k - i
btw,共享同一底層數(shù)組的切片會(huì)導(dǎo)致如果修改了其中一個(gè)切片的索引的數(shù)據(jù)妹沙,則另外一個(gè)對(duì)應(yīng)切片的數(shù)據(jù)也會(huì)被修改偶洋。
// 創(chuàng)建一個(gè)整型切片
// 其長(zhǎng)度和容量都是 5 個(gè)元素
slice := []int{10, 20, 30, 40, 50}
// 創(chuàng)建一個(gè)新切片
// 其長(zhǎng)度是 2 個(gè)元素,容量是 4 個(gè)元素
newSlice := slice[1:3]
// 修改 newSlice 索引為 1 的元素
// 同時(shí)也修改了原來(lái)的 slice 的索引為 2 的元素
newSlice[1] = 35
對(duì)于 slice[i:j:k] 或 [2:3:4]
長(zhǎng)度: j – i 或 3 - 2 = 1
容量: k – i 或 4 - 2 = 2
切片只能訪(fǎng)問(wèn)到其長(zhǎng)度內(nèi)的元素距糖。切片有額外的容量是很好玄窝,但是如果不能把這些容量合并到切片的長(zhǎng)度里牵寺,這些容量就沒(méi)有用處。
##### 映射
映射是一種數(shù)據(jù)結(jié)構(gòu)恩脂,用于存儲(chǔ)一系列無(wú)序的鍵值對(duì)帽氓。
映射里基于鍵來(lái)存儲(chǔ)值。
映射功能強(qiáng)大的地方是俩块,能夠基于鍵快速檢索數(shù)據(jù)黎休。鍵就像索引一樣,指向該鍵關(guān)聯(lián)的值典阵。
??1.數(shù)組是構(gòu)造切片和映射的基石奋渔。
??2.Go 語(yǔ)言里切片經(jīng)常用來(lái)處理數(shù)據(jù)的集合,映射用來(lái)處理具有鍵值對(duì)結(jié)構(gòu)的數(shù)據(jù)壮啊。
??3.函數(shù) make 可以創(chuàng)建切片和映射嫉鲸,并指定原始的長(zhǎng)度和容量。也可以直接使用切片和映射字面量歹啼,或者使用字面量作為變量的初始值玄渗。
??4.有容量限制,不過(guò)可以使用內(nèi)置的 append 函數(shù)擴(kuò)展容量狸眼。
??5.的增長(zhǎng)沒(méi)有容量或者任何限制藤树。
??6.函數(shù) len 可以用來(lái)獲取切片或者映射的長(zhǎng)度。
??7.函數(shù) cap 只能用于切片拓萌。
??8.組合岁钓,可以創(chuàng)建多維數(shù)組和多維切片。也可以使用切片或者其他映射作為映射的值微王。但是切片不能用作映射的鍵屡限。
??9.片或者映射傳遞給函數(shù)成本很小,并且不會(huì)復(fù)制底層的數(shù)據(jù)結(jié)構(gòu)
#### 2) 并發(fā)編程(goroutine炕倘、channel)
當(dāng)一個(gè)函數(shù)創(chuàng)建為 goroutine時(shí)钧大, Go 會(huì)將其視為一個(gè)獨(dú)立的工作單元。這個(gè)單元會(huì)被調(diào)度到可用的邏輯處理器上執(zhí)行罩旋。
Go 語(yǔ)言的并發(fā)同步模型來(lái)自一個(gè)叫作通信順序進(jìn)程(Communicating Sequential Processes啊央, CSP)的范型(paradigm)。 CSP 是一種消息傳遞模型涨醋,通過(guò)在 goroutine 之間傳遞數(shù)據(jù)來(lái)傳遞消息瓜饥,而不是對(duì)數(shù)據(jù)進(jìn)行加鎖來(lái)實(shí)現(xiàn)同步訪(fǎng)問(wèn)。
用于在 goroutine 之間同步和傳遞數(shù)據(jù)的關(guān)鍵數(shù)據(jù)類(lèi)型叫作通道(channel)浴骂。
創(chuàng)建goroutine的例子
01// 這個(gè)示例程序展示如何創(chuàng)建 goroutine
02 // 以及調(diào)度器的行為
03 package main
04
05 import (
06 "fmt"
07 "runtime"
08 "sync"
09 )
10
11 // main 是所有 Go 程序的入口
12 func main() {
13 // 分配一個(gè)邏輯處理器給調(diào)度器使用
14 runtime.GOMAXPROCS(1)
15
16 // wg 用來(lái)等待程序完成
17 // 計(jì)數(shù)加 2压固,表示要等待兩個(gè) goroutine
18 var wg sync.WaitGroup
19 wg.Add(2)
20
21 fmt.Println("Start Goroutines")
22
23 // 聲明一個(gè)匿名函數(shù),并創(chuàng)建一個(gè) goroutine
24 go func() {
25 // 在函數(shù)退出時(shí)調(diào)用 Done 來(lái)通知 main 函數(shù)工作已經(jīng)完成
26 defer wg.Done()
27
28 // 顯示字母表 3 次
29 for count := 0; count < 3; count++ {
30 for char := 'a'; char < 'a'+26; char++ {
31 fmt.Printf("%c ", char)
32 }
33 }
34 }()
35
36 // 聲明一個(gè)匿名函數(shù)靠闭,并創(chuàng)建一個(gè) goroutine
37 go func() {
38 // 在函數(shù)退出時(shí)調(diào)用 Done 來(lái)通知 main 函數(shù)工作已經(jīng)完成
39 defer wg.Done()
40
41 // 顯示字母表 3 次
42 for count := 0; count < 3; count++ {
43 for char := 'A'; char < 'A'+26; char++ {
44 fmt.Printf("%c ", char)
45 }
46 }
47 }()
48
49 // 等待 goroutine 結(jié)束
50 fmt.Println("Waiting To Finish")
51 wg.Wait()
52
53 fmt.Println("\nTerminating Program")
54 }
關(guān)于該程序中涉及到的defer
Defer is used to ensure that a function call is performed later in a program’s execution, usually for purposes of cleanup. defer is often used where e.g. ensure and finally would be used in other languages.
defer的思想類(lèi)似于C++中的析構(gòu)函數(shù)帐我,不過(guò)Go語(yǔ)言中“析構(gòu)”的不是對(duì)象,而是函數(shù)愧膀,defer就是用來(lái)添加函數(shù)結(jié)束時(shí)執(zhí)行的語(yǔ)句拦键。注意這里強(qiáng)調(diào)的是添加,而不是指定檩淋,因?yàn)椴煌贑++中的析構(gòu)函數(shù)是靜態(tài)的芬为,Go中的defer是動(dòng)態(tài)的。
Go 標(biāo)準(zhǔn)庫(kù)的 runtime 包里有一個(gè)名為GOMAXPROCS
的函數(shù)蟀悦,通過(guò)它可以指定調(diào)度器可用的邏輯處理器的數(shù)量媚朦。用這個(gè)函數(shù),可以給每個(gè)可用的物理處理器在
運(yùn)行的時(shí)候分配一個(gè)邏輯處理器日戈。
#### 3) socket編程(net包)
#### 4) http編程(http包)
#### 5) json解析(json包以及其它第三方包)
json的簡(jiǎn)單介紹
[JSON](JavaScript Object Notation, JS 對(duì)象簡(jiǎn)譜) 是一種輕量級(jí)的數(shù)據(jù)交換格式询张。它基于 [ECMAScript] (歐洲計(jì)算機(jī)協(xié)會(huì)制定的js規(guī)范)的一個(gè)子集,采用完全獨(dú)立于編程語(yǔ)言的文本格式來(lái)存儲(chǔ)和表示數(shù)據(jù)浙炼。簡(jiǎn)潔和清晰的層次結(jié)構(gòu)使得 JSON 成為理想的數(shù)據(jù)交換語(yǔ)言份氧。 易于人閱讀和編寫(xiě),同時(shí)也易于機(jī)器解析和生成弯屈,并有效地提升網(wǎng)絡(luò)傳輸效率蜗帜。
#### 6) 流處理(binary包)