一、Go語言背景和發(fā)展
1.軟件開發(fā)的新挑戰(zhàn)
- 多核硬件架構(gòu)
- 超大規(guī)模分布式計(jì)算集群
- Web模式導(dǎo)致的前所未有的開發(fā)規(guī)模和更新速度
2.Go的三位創(chuàng)始人
- Rob Pike:Unix的早期開發(fā)者,UTF-8創(chuàng)始人
- Ken Thompson:Unix的創(chuàng)始人翠胰,C語言創(chuàng)始人,1983年獲圖靈獎(jiǎng)
- Robert Griesemer:Google V8 JS Engine開發(fā)者竖慧,Hot Spot開發(fā)者
3.Go語言特點(diǎn)
- 簡單:Go只有25個(gè)關(guān)鍵字飞主;特別是對(duì)于一些復(fù)雜編程任務(wù)如:并發(fā)編程,內(nèi)存管理前鹅,Go語言有內(nèi)置的并發(fā)支持及GC
- 高效:Go是編譯的靜態(tài)類型語言摘悴,并且可以通過指針進(jìn)行直接內(nèi)存訪問
- 生產(chǎn)力:簡單清新的依賴管理,簡單清新的語法舰绘,以及獨(dú)特的接口類型
二蹂喻、第一個(gè)Go程序
[圖片上傳失敗...(image-8dd3db-1556979504914)]
1.應(yīng)用程序入口
- 必須是main包
- package main
- 必須是main方法
- func main()
- 文件名不一定是main.go
2.退出返回值
- Go中main函數(shù)不支持任何返回值
- 通過os.Exit來返回狀態(tài)
3.獲取命令行參數(shù)
- main函數(shù)不支持傳入?yún)?shù)
- 在程序中直接通過os.Args獲取命令行參數(shù)
4.基本數(shù)據(jù)類型
- bool
- string
- int int8 int16 int32 int64
- uint uint8 uint16 uint32 uint64
- byte // alias for uint8
- rune
- float32 float64
- complex64 complex128
與其他主要編程的差異:
- Go語言不允許隱式類型轉(zhuǎn)換
- 別名和原有類型也不能進(jìn)行隱式類型轉(zhuǎn)換
類型的預(yù)定義值:
- math.MaxInt64
- math.MaxFloat64
- math.MaxUint32
指針類型
- 不支持指針運(yùn)算
- string是值類型,其默認(rèn)的初始值為空字符串除盏,而不是nil
5.運(yùn)算符
用 == 比較數(shù)組
- 相同維數(shù)且含有相同個(gè)數(shù)元素的數(shù)組才可以比較
- 每個(gè)元素都相同的才相等
按位清零運(yùn)算符
- &^
6.循環(huán)
Go語言僅支持循環(huán)關(guān)鍵字for
for i := 0; i <= 9; I++
7.if條件
- condition表達(dá)式結(jié)果必須為布爾值
- 支持變量賦值:
if var declaration; condition {
}
8.switch條件
- 條件表達(dá)式不限制為常量或者整數(shù)
- 單個(gè)case中叉橱,可以出現(xiàn)多個(gè)結(jié)果選項(xiàng),使用逗號(hào)分隔
- 與C語言等規(guī)則相反者蠕,Go語言不需要用break來明確退出一個(gè)case
- 可以不設(shè)定switch之后的條件表達(dá)式,在此種情況下掐松,整個(gè)switch結(jié)構(gòu)與多個(gè)if...else...的邏輯作用等同
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
//break
case "linux":
fmt.Println("Linux.")
default:
fmt.Println("%s.",os)
}
switch {
case 0 <= Num && Num <= 3:
fmt.Printf("0-3")
case 4 <= Num && Num <= 6:
fmt.Printf("4-6")
}
switch I {
case 0,2:
fmt.Printf("0-2")
case 1,3:
fmt.Printf("1-3")
}
9.Map元素的訪問
在訪問的Key不存在時(shí)踱侣,仍會(huì)返回零值,不能通過返回nil來判斷元素是否存在
if v,ok := map1["key1"]; ok{
} else {
}
map的遍歷
for k,v := range map1{
}
10.Map與工廠模式
- Map的value可以是一個(gè)方法
- 與Go的Dock type接口方式一起大磺,可以方便的實(shí)現(xiàn)單一方法對(duì)象的工廠模式
11.實(shí)現(xiàn)Set
Go的內(nèi)置集合中沒有Set實(shí)現(xiàn)抡句,可以map[type]bool
- 元素的唯一性
- 基本操作
- 添加元素
- 判斷元素是否存在
- 刪除元素
- 元素個(gè)數(shù)
12.字符串
- string是數(shù)據(jù)類型,不是引用或指針類型
- string是只讀的byte slice杠愧,len函數(shù)可以它所包含的byte數(shù)
- string的byte數(shù)組可以存放任何數(shù)據(jù)
13.Unicode UTF8
- Unicode是一種字符集(code point)
- UTF8是unicode的存儲(chǔ)實(shí)現(xiàn)(轉(zhuǎn)換為字節(jié)序列的規(guī)則)
14.常用字符串函數(shù)
- strings包(https://golang.org/pkg/strings/)
- strconv包(https://golang.org/pkg/strconv)
15.函數(shù)是一等公民
- 可以有多個(gè)返回值
- 所有參數(shù)都是值傳遞:slice待榔、map、channel會(huì)有傳引用的錯(cuò)覺
- 函數(shù)可以作為變量的值
- 函數(shù)可以作為參數(shù)和返回值
16.Go接口
- 接口為非入侵性流济,實(shí)現(xiàn)不依賴接口定義
- 所以接口的定義可以包含在接口使用者包內(nèi)
17.空接口與斷言
- 空接口可以表示任何類型
- 通過斷言來將空接口轉(zhuǎn)換為制定類型
v, ok := p.(int) //ok=true時(shí)為轉(zhuǎn)換成功
18.Go接口最佳實(shí)踐
- 傾向于使用小的接口定義锐锣,很多接口只包含一個(gè)方法
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int,err error)
}
- 較大的接口定義,可以由多個(gè)小接口定義組合而成
type ReadWriter interface {
Reader
Writer
}
- 只依賴于必要功能的最小接口
func StoreData(reader Reader) error {
......
}
19.編寫好的錯(cuò)誤處理
- 沒有異常機(jī)制
- error類型實(shí)現(xiàn)了error接口
type error interface {
Error() string
}
- 可以通過errors.New來快速創(chuàng)建錯(cuò)誤實(shí)例
errors.New("")
20.panic
- panic用于不可以恢復(fù)的錯(cuò)誤
- panic退出前會(huì)執(zhí)行defer指定的內(nèi)容
panic vs os.Exit
- os.Exit 退出時(shí)不會(huì)調(diào)用defer指定的函數(shù)
- os.Exit 退出時(shí)不輸出當(dāng)前調(diào)用棧信息
recover
defer func() {
if err := recover(); err != nil {
//恢復(fù)錯(cuò)誤
}
}()
21.package
- 基本復(fù)用模塊單元
- 以首字母大寫來表明可被包外代碼訪問
- 代碼的package可以和所在的目錄不一致
- 同一目錄里的Go代碼的package要保持一致
- 通過go get來獲取遠(yuǎn)程依賴
- go get -u強(qiáng)制從網(wǎng)絡(luò)更新遠(yuǎn)程依賴
- 注意代碼在Github上的組織形式绳瘟,以適應(yīng)go get
- 直接以代碼路徑開始雕憔,不要有src
22.init方法
- 在main被執(zhí)行前,所有依賴的package的init方法都會(huì)被執(zhí)行
- 不同包的init函數(shù)按照包導(dǎo)入的依賴關(guān)系決定執(zhí)行順序
- 每個(gè)包可以有多個(gè)init函數(shù)
- 包的每個(gè)源文件也可以有多個(gè)init函數(shù)糖声,這點(diǎn)比較特殊
23.依賴管理
Go未解決的依賴問題
- 同一環(huán)境下斤彼,不同項(xiàng)目使用同一包的不同版本
- 無法管理對(duì)包的特定版本的依賴
vendor路徑
<p>隨著Go1.5 release 版本的發(fā)布分瘦,vendor目錄被添加到除了GOPATH和GOROOT之外的依賴目錄查找的解決方案。在Go 1.6之前琉苇,你需要手動(dòng)的設(shè)置環(huán)境變量</p>
查找依賴包路徑的解決方案如下:
- 當(dāng)前包下的vendor目錄
- 向上級(jí)目錄查找嘲玫,直到找到src下的vendor目錄
- 在GOPATH下面查找依賴包
- 在GOROOT目錄下查找
常用的依賴管理
- godep https://github.com/tools/godep
- glide https://github.com/Masterminds/glide
- dep https://github.com/golang/dep
三、并發(fā)機(jī)制
1.Thread vs. Groutine
- 創(chuàng)建時(shí)默認(rèn)的stack的大小
- JDK5以后Java Thread stack默認(rèn)為1M
- Groutine的Stack初始化大小為2K
- 和KSE (kernel Space Entity)的對(duì)應(yīng)關(guān)系
- Java Thread是1:1
- Groutine是M:N
2.Lock
package sync
Mutex
RWLock
WaiteGroup
3.CSP并發(fā)機(jī)制
CSP vs. Actor
- 和Actor的直接通訊不同并扇,CSP模式則是通過Channel進(jìn)行通訊的去团,更松耦合一些
- Go中channel是有容量限制并且獨(dú)立于處理Groutine,而如Erlang拜马,Actor模式中的mailbox容量是無限的渗勘,接受進(jìn)程也總是被動(dòng)處理消息
4.select
多渠道的選擇
select {
case ret := <- retCh1:
t.Logf("result %s", ret)
case ret := <- retCh2:
t.Logf("result %s", ret)
default:
t.Error("No one returned")
}
超時(shí)控制
select {
case ret := <- retCh:
t.Logf("result %s", ret)
case <- time.After(time.Second * 1):
t.Error("time out")
}
5.channel的關(guān)閉
- 向關(guān)閉的channel發(fā)送數(shù)據(jù),會(huì)導(dǎo)致panic
- v,ok <- ch;ok為bool值俩莽,true表示正常接受旺坠,false表示通道關(guān)閉
- 所有的channel接受者都會(huì)在channel關(guān)閉時(shí),立刻從阻塞等待中返回且上述ok值為false扮超。這個(gè)廣播機(jī)制常被利用取刃,進(jìn)行向多個(gè)訂閱者同時(shí)發(fā)送信號(hào)。如:退出信號(hào)
6.Context與任務(wù)取消
- 根Context:通過context.Background()創(chuàng)建
- 子Context:context.WithCancel(parentContext)創(chuàng)建
- ctx,cancel := context.WithCancel(context.Background())
- 當(dāng)前Context被取消時(shí)出刷,基于他的子context都會(huì)被取消
- 接收取消通知 <- ctx.Done()
7.單例模式(懶漢式璧疗,線程安全)
var once sync.Once
var obj *SingletonObj
func GetSingletonObj() *SingletonObj {
once.Do(func(){
fmt.Println("Create Singleton obj.")
obj = &SingletonObj{}
})
return obj
}
8、對(duì)象池
9馁龟、sync.Pool總結(jié)
- 適合于通過復(fù)用崩侠,降低復(fù)雜對(duì)象的創(chuàng)建和GC代價(jià)
- 協(xié)程安全,會(huì)有鎖的開銷
- 生命周期受GC影響坷檩,不適合于做連接池等却音,需自己管理生命周期的資源的池化
10、單元測試框架
Benchmark
BDD in Go
項(xiàng)目網(wǎng)站
https://github.com/smartystreets/goconvey
安裝
go get -u github.com/smartystreets/goconvey/convey
啟動(dòng)WEB UI
$GOPATH/bin/goconvey
四矢炼、其他
1系瓢、reflect.TypeOf vs. reflect.ValueOf
- reflect.TypeOf 返回類型(reflect.Type)
- reflect.ValueOf 返回值 (reflect.Value)
- 可以從reflect.Value獲得類型
- 通過kind的來判斷類型
2、利用反射編寫靈活的代碼
- 按名字訪問結(jié)構(gòu)的成員
reflect.ValueOf(*e).FieldByName("Name")
- 按名字訪問結(jié)構(gòu)的方法
reflect.ValueOf(e).MethodByName("UpdateAge").Call([]reflect.Value{reflect.ValueOf(1)})
3句灌、架構(gòu)設(shè)計(jì)模式:Pipe-Filter模式
- 非常適合與數(shù)據(jù)處理及數(shù)據(jù)分析系統(tǒng)
- Filter封裝數(shù)據(jù)處理的功能
- 松耦合:Filter只跟數(shù)據(jù)(格式)耦合
- Pipe用于連接Filter傳遞數(shù)據(jù)或者在異步處理過程中緩沖數(shù)據(jù)流(進(jìn)程內(nèi)同步調(diào)用夷陋,pipe演變?yōu)閿?shù)據(jù)在方法調(diào)用間傳遞)
4、架構(gòu)設(shè)計(jì)模式:Micro Kernel
特點(diǎn)
- 易于擴(kuò)展
- 錯(cuò)誤隔離
- 保持架構(gòu)一致性
要點(diǎn)
- 內(nèi)核包含公共流程或通用邏輯
- 將可變或可擴(kuò)展部分規(guī)劃為擴(kuò)展點(diǎn)
- 抽象擴(kuò)展點(diǎn)行為胰锌,定義接口
- 利用插件進(jìn)行擴(kuò)展
5骗绕、更快的JSON解析
EasyJSON采用代碼生成而非反射
- 安裝
go get -u github.com/mailru/easyjson/ - 使用
easyjson -all <結(jié)構(gòu)定義>.go
6、Http路由規(guī)則
- URL分為兩種匕荸,末尾是/:表示一個(gè)子樹爹谭,后面可以跟其他子路徑;末尾不是/榛搔,表示一個(gè)葉子诺凡,固定的路徑(以/結(jié)尾的URL可以匹配它的任何子路徑东揣,比如/images會(huì)匹配/images/cute-cat.jpg)
- 它采用最長匹配原則,如果有多個(gè)匹配腹泌,一定采用匹配路徑最長的那個(gè)進(jìn)行處理
- 如果沒有找到任何匹配項(xiàng)嘶卧,會(huì)返回404錯(cuò)誤
7、更好的Router
htttps://github.com/julienschmidt/httprouter
8凉袱、性能分析工具
準(zhǔn)備工作
- 安裝graphviz
- brew install graphviz
- 將PATH
- Mac OS:在 .bash_profile 中修改路徑
- 安裝go-torch
- go get github.com/uber/go-torch
- 下載并復(fù)制 flamegraph.pl (https://github.com/brendangregg/FlameGraph)至$GOPATH/bin 路徑下
- 將PATH
通過文件方式輸出Profile
- 靈活性高芥吟,適用于特定代碼段的分析
- 通過手動(dòng)調(diào)用 runtime/pprof 的API
- API相關(guān)文檔 https://studygolang.com/static/pkgdoc/pkg/runtime_pprof.htm
- go tool pprof [binary] [binary.prof]
9、性能調(diào)優(yōu)
常見分析指標(biāo)
- Wall Time
- CPU Time
- Block Time
- Memory Time
- GC times/time spent