本書從實(shí)踐出發(fā)講解Go語言的進(jìn)階知識(shí)。本書共6章,第1章簡(jiǎn)單回顧Go語言的發(fā)展歷史;第2章和第3章系統(tǒng)地介紹CGO編程和Go匯編語言的用法;第4章對(duì)RPC和Protobuf技術(shù)進(jìn)行深入介紹,并講述如何打造一個(gè)自己的RPC系統(tǒng);第5章介紹工業(yè)級(jí)環(huán)境的Web系統(tǒng)的設(shè)計(jì)和相關(guān)技術(shù);第6章介紹Go語言在分布式領(lǐng)域的一些編程技術(shù)。書中還涉及CGO和匯編方面的知識(shí),其中CGO能夠幫助讀者繼承的軟件遺產(chǎn),而在深入學(xué)習(xí)Go運(yùn)行時(shí),匯編對(duì)于理解各種語法設(shè)計(jì)的底層實(shí)現(xiàn)是必不可少的知識(shí)。此外,本書還包含一些緊跟潮流的內(nèi)容,介紹開源界流行的gRPC及其相關(guān)應(yīng)用,講述Go Web框架中的基本實(shí)現(xiàn)原理和大型Web項(xiàng)目中的技術(shù)要點(diǎn),引導(dǎo)讀者對(duì)Go語言進(jìn)行更深入的應(yīng)用。 本書適合對(duì)Go語言的應(yīng)用已經(jīng)有一些心得,并希望能夠深入理解底層實(shí)現(xiàn)原理或者是希望能夠在Web開發(fā)方面結(jié)合Go語言來實(shí)現(xiàn)進(jìn)階學(xué)習(xí)的技術(shù)人員學(xué)習(xí)和參考。
截至2019年,Go語言已歷經(jīng)10 年,國(guó)內(nèi)互聯(lián)網(wǎng)公司的新興項(xiàng)目已經(jīng)在逐漸向Go語言生態(tài)轉(zhuǎn)移。隨著用戶的不斷積累,Go語言相關(guān)教程隨之增加,這些教程主要涵蓋Go語言基礎(chǔ)編程、Web編程、并發(fā)編程和內(nèi)部源碼剖析等諸多內(nèi)容。 本書聚焦于主流Go語言書中缺失的或刻意回避的部分主題,主要面向希望深入了解Go語言,特別是對(duì)Go語言和其他語言的混合編程、Go匯編語言的工作機(jī)制、構(gòu)造Web框架和分布式開發(fā)等領(lǐng)域感興趣的學(xué)生、工程師和研究人員。閱讀本書需要讀者對(duì)Go語言有一定的認(rèn)識(shí)和使用經(jīng)驗(yàn)。 本書關(guān)于CGO編程和Go匯編語言的講解在中國(guó)乃至全球Go語言出版物中是非常有特色的。 本書主要內(nèi)容 ● Go語言演化歷史。 ● CGO編程技術(shù)。 ● Go匯編語言。 ● RPC和gRPC。 ● 構(gòu)造Web框架的方法。 ● 分布式系統(tǒng)。
序一
互聯(lián)網(wǎng)時(shí)代的來臨,改變甚至顛覆了很多東西。從前,一臺(tái)主機(jī)就能搞定一切;而在互聯(lián)網(wǎng)時(shí)代,后臺(tái)由大量分布式系統(tǒng)構(gòu)成,任何單個(gè)后臺(tái)服務(wù)器節(jié)點(diǎn)的故障都不會(huì)影響整個(gè)系統(tǒng)的正常運(yùn)行。以七牛云、阿里云和騰訊云為代表的云廠商的出現(xiàn)和崛起,標(biāo)志著云時(shí)代的到來。在云時(shí)代,掌握分布式編程已經(jīng)成為軟件工程師的基本技能,而基于Go語言構(gòu)建的Docker、Kubernetes等系統(tǒng)正是將云時(shí)代推向頂峰的關(guān)鍵力量。
今天,Go語言已歷經(jīng)十年,最初的追隨者也已經(jīng)逐漸成長(zhǎng)為Go語言資深用戶。隨著資深用戶的不斷積累,Go語言相關(guān)教程隨之增加,在內(nèi)容層面主要涵蓋Go語言基礎(chǔ)編程、Web編程、并發(fā)編程和內(nèi)部源碼剖析等諸多領(lǐng)域。
本書作者是國(guó)內(nèi)第一批Go語言實(shí)踐者和Go語言代碼貢獻(xiàn)者,創(chuàng)建了Go語言中國(guó)討論組,并組織了早期Go語言相關(guān)中文文檔的翻譯工作。作者從2011年開始分享Go語言和C/C 語言混合編程技術(shù)。本書匯集了作者多年來學(xué)習(xí)和使用Go語言的經(jīng)驗(yàn),內(nèi)容涵蓋CGO特性、Go匯編語言、RPC實(shí)現(xiàn)、Protobuf插件實(shí)現(xiàn)、Web框架實(shí)現(xiàn)、分布式系統(tǒng)等高階主題。其中,CGO特性實(shí)現(xiàn)了Go語言對(duì)C語言和C 語言混合編程的支持,使Go語言可以無縫繼承C/C 世界數(shù)十年來積累的巨大軟件資產(chǎn)。Go匯編語言更是提供了直接調(diào)用底層機(jī)器指令的方法,讓我們可以最大限度地提升程序中熱點(diǎn)代碼的性能。
目前,國(guó)內(nèi)互聯(lián)網(wǎng)公司的新興項(xiàng)目已經(jīng)在逐漸向Go語言生態(tài)轉(zhuǎn)移,大型分布式系統(tǒng)的開發(fā)實(shí)戰(zhàn)經(jīng)驗(yàn)也是大家關(guān)心的熱點(diǎn)。這些高階或前沿特性正是本書所關(guān)注的課題,在這些方面作者通過不斷鉆研和實(shí)踐積累了很多寶貴經(jīng)驗(yàn)。
總體來說,本書適合有一定Go語言經(jīng)驗(yàn),并想深入了解Go語言各種高級(jí)用法的開發(fā)人員。對(duì)于Go語言新手,建議在閱讀本書前先閱讀一些基礎(chǔ)Go語言編程圖書,例如D&K的The Go Programming Language。
最后,感謝作者在Go語言領(lǐng)域的筆耕不輟和突出貢獻(xiàn),時(shí)代需要的正是這樣對(duì)于新興技術(shù)不斷關(guān)注、鉆研和推動(dòng)的布道者。七牛云作為一家技術(shù)領(lǐng)先的科技公司,也將在這條布道者的道路上不斷前進(jìn),為推動(dòng)科技的發(fā)展、中國(guó)企業(yè)的云落地和行業(yè)的數(shù)字化轉(zhuǎn)型貢獻(xiàn)自己的力量。
許式偉,七牛云CEO
2019年5月于上海
柴樹杉,國(guó)內(nèi)較早的一批Go語言愛好者,Go語言代碼貢獻(xiàn)者。對(duì)WebAssembly技術(shù)有一定研究,除本書外還著有《WebAssembly標(biāo)準(zhǔn)入門》一書。GitHub賬號(hào)為chai2010。 曹春暉,在Web 領(lǐng)域工作多年,開源愛好者。對(duì)大型網(wǎng)站系統(tǒng)的架構(gòu)和相關(guān)工具的實(shí)現(xiàn)很感興趣,并且有一些研究成果。目前在滴滴平臺(tái)技術(shù)部工作。
目 錄
第 1章 語言基礎(chǔ) 1
1.1 Go語言創(chuàng)世紀(jì) 1
1.1.1 來自貝爾實(shí)驗(yàn)室特有基因 3
1.1.2 你好,世界 4
1.2 Hello, World的革命 5
1.2.1 B語言Ken Thompson, 1969 5
1.2.2 C語言Dennis Ritchie,19721989 5
1.2.3 NewsqueakRob Pike, 1989 7
1.2.4 AlefPhil Winterbottom, 1993 9
1.2.5 LimboSean Dorward, Phil Winterbottom, Rob Pike, 1995 10
1.2.6 Go語言20072009 11
1.2.7 你好,世界!V2.0 13
1.3 數(shù)組、字符串和切片 13
1.3.1 數(shù)組 14
1.3.2 字符串 17
1.3.3 切片 21
1.4 函數(shù)、方法和接口 27
1.4.1 函數(shù) 27
1.4.2 方法 31
1.4.3 接口 35
1.5 面向并發(fā)的內(nèi)存模型 39
1.5.1 Goroutine和系統(tǒng)線程 40
1.5.2 原子操作 40
1.5.3 順序一致性內(nèi)存模型 44
1.5.4 初始化順序 45
1.5.5 Goroutine的創(chuàng)建 46
1.5.6 基于通道的通信 46
1.5.7 不靠譜的同步 48
1.6 常見的并發(fā)模式 49
1.6.1 并發(fā)版本的Hello, World 50
1.6.2 生產(chǎn)者/消費(fèi)者模型 52
1.6.3 發(fā)布/訂閱模型 53
1.6.4 控制并發(fā)數(shù) 56
1.6.5 贏者為王 57
1.6.6 素?cái)?shù)篩 58
1.6.7 并發(fā)的安全退出 59
1.6.8 context包 62
1.7 錯(cuò)誤和異!64
1.7.1 錯(cuò)誤處理策略 65
1.7.2 獲取錯(cuò)誤的上下文 67
1.7.3 錯(cuò)誤的錯(cuò)誤返回 69
1.7.4 剖析異!70
1.8 補(bǔ)充說明 73
第 2章 CGO編程 74
2.1 快速入門 74
2.1.1 最簡(jiǎn)CGO程序 74
2.1.2 基于C標(biāo)準(zhǔn)庫(kù)函數(shù)輸出字符串 75
2.1.3 使用自己的C函數(shù) 75
2.1.4 C代碼的模塊化 76
2.1.5 用Go重新實(shí)現(xiàn)C函數(shù) 77
2.1.6 面向C接口的Go編程 78
2.2 CGO基礎(chǔ) 79
2.2.1 import "C"語句 79
2.2.2 #cgo語句 81
2.2.3 build標(biāo)志條件編譯 82
2.3 類型轉(zhuǎn)換 83
2.3.1 數(shù)值類型 83
2.3.2 Go字符串和切片 85
2.3.3 結(jié)構(gòu)體、聯(lián)合和枚舉類型 86
2.3.4 數(shù)組、字符串和切片 89
2.3.5 指針間的轉(zhuǎn)換 91
2.3.6 數(shù)值和指針的轉(zhuǎn)換 92
2.3.7 切片間的轉(zhuǎn)換 93
2.4 函數(shù)調(diào)用 94
2.4.1 Go調(diào)用C函數(shù) 94
2.4.2 C函數(shù)的返回值 94
2.4.3 void函數(shù)的返回值 95
2.4.4 C調(diào)用Go導(dǎo)出函數(shù) 96
2.5 內(nèi)部機(jī)制 97
2.5.1 CGO生成的中間文件 97
2.5.2 Go調(diào)用C函數(shù) 98
2.5.3 C調(diào)用Go函數(shù) 101
2.6 實(shí)戰(zhàn):封裝qsort 103
2.6.1 認(rèn)識(shí)qsort()函數(shù) 103
2.6.2 將qsort()函數(shù)從Go包導(dǎo)出 104
2.6.3 改進(jìn):閉包函數(shù)作為比較函數(shù) 106
2.6.4 改進(jìn):消除用戶對(duì)unsafe包的依賴 108
2.7 CGO內(nèi)存模型 110
2.7.1 Go訪問C內(nèi)存 110
2.7.2 C臨時(shí)訪問傳入的Go內(nèi)存 111
2.7.3 C長(zhǎng)期持有Go指針對(duì)象 113
2.7.4 導(dǎo)出C函數(shù)不能返回Go內(nèi)存 115
2.8 C 類包裝 117
2.8.1 C 類到Go語言對(duì)象 117
2.8.2 Go語言對(duì)象到C 類 121
2.8.3 徹底解放C 的this指針 125
2.9 靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù) 126
2.9.1 使用C靜態(tài)庫(kù) 126
2.9.2 使用C動(dòng)態(tài)庫(kù) 128
2.9.3 導(dǎo)出C靜態(tài)庫(kù) 129
2.9.4 導(dǎo)出C動(dòng)態(tài)庫(kù) 131
2.9.5 導(dǎo)出非main包的函數(shù) 131
2.10 編譯和鏈接參數(shù) 133
2.10.1 編譯參數(shù):CFLAGS/CPPFLAGS/CXXFLAGS 133
2.10.2 鏈接參數(shù):LDFLAGS 133
2.10.3 pkg-config 133
2.10.4 go get鏈 134
2.10.5 多個(gè)非main包中導(dǎo)出C函數(shù) 135
2.11 補(bǔ)充說明 135
第3章 Go匯編語言 136
3.1 快速入門 136
3.1.1 實(shí)現(xiàn)和聲明 136
3.1.2 定義整數(shù)變量 137
3.1.3 定義字符串變量 138
3.1.4 定義main()函數(shù) 141
3.1.5 特殊字符 141
3.1.6 沒有分號(hào) 142
3.2 計(jì)算機(jī)結(jié)構(gòu) 142
3.2.1 圖靈機(jī)和BrainFuck語言 143
3.2.2 《人力資源機(jī)器》游戲 144
3.2.3 X86-64體系結(jié)構(gòu) 145
3.2.4 Go匯編中的偽寄存器 146
3.2.5 X86-64指令集 147
3.3 常量和全局變量 150
3.3.1 常量 150
3.3.2 全局變量 150
3.3.3 變量的內(nèi)存布局 156
3.3.4 標(biāo)識(shí)符規(guī)則和特殊標(biāo)志 157
3.3.5 小結(jié) 158
3.4 函數(shù) 158
3.4.1 基本語法 158
3.4.2 函數(shù)參數(shù)和返回值 160
3.4.3 參數(shù)和返回值的內(nèi)存布局 161
3.4.4 函數(shù)中的局部變量 163
3.4.5 調(diào)用其他函數(shù) 165
3.4.6 宏函數(shù) 166
3.5 控制流 167
3.5.1 順序執(zhí)行 167
3.5.2 if/goto跳轉(zhuǎn) 169
3.5.3 for循環(huán) 171
3.6 再論函數(shù) 172
3.6.1 函數(shù)調(diào)用規(guī)范 172
3.6.2 高級(jí)匯編語言 173
3.6.3 PCDATA和FUNCDATA 176
3.6.4 方法函數(shù) 177
3.6.5 遞歸函數(shù): 1到n求和 178
3.6.6 閉包函數(shù) 180
3.7 匯編語言的威力 182
3.7.1 系統(tǒng)調(diào)用 182
3.7.2 直接調(diào)用C函數(shù) 184
3.7.3 AVX指令 185
3.8 例子:Goroutine ID 187
3.8.1 故意設(shè)計(jì)沒有g(shù)oid 187
3.8.2 純Go方式獲取goid 187
3.8.3 從g結(jié)構(gòu)體獲取goid 189
3.8.4 獲取g結(jié)構(gòu)體對(duì)應(yīng)的接口對(duì)象 190
3.8.5 goid的應(yīng)用:局部存儲(chǔ) 192
3.9 Delve調(diào)試器 194
3.9.1 Delve入門 194
3.9.2 調(diào)試匯編程序 198
3.10 補(bǔ)充說明 201
第4章 RPC和Protobuf 203
4.1 RPC入門 203
4.1.1 RPC版Hello, World 203
4.1.2 更安全的RPC接口 205
4.1.3 跨語言的RPC 207
4.1.4 HTTP上的RPC 209
4.2 Protobuf 210
4.2.1 Protobuf入門 210
4.2.2 定制代碼生成插件 212
4.2.3 自動(dòng)生成完整的RPC代碼 215
4.3 玩轉(zhuǎn)RPC 218
4.3.1 客戶端RPC的實(shí)現(xiàn)原理 218
4.3.2 基于RPC實(shí)現(xiàn)監(jiān)視功能 220
4.3.3 反向RPC 222
4.3.4 上下文信息 223
4.4 gRPC入門 224
4.4.1 gRPC技術(shù)!225
4.4.2 gRPC入門 225
4.4.3 gRPC流 227
4.4.4 發(fā)布和訂閱模式 229
4.5 gRPC進(jìn)階 233
4.5.1 證書認(rèn)證 233
4.5.2 Token認(rèn)證 236
4.5.3 截取器 238
4.5.4 和Web服務(wù)共存 240
4.6 gRPC和Protobuf擴(kuò)展 241
4.6.1 驗(yàn)證器 241
4.6.2 REST接口 244
4.6.3 Nginx 246
4.7 pbgo:基于Protobuf的框架 246
4.7.1 Protobuf擴(kuò)展語法 246
4.7.2 插件中讀取擴(kuò)展信息 248
4.7.3 生成REST代碼 249
4.7.4 啟動(dòng)REST服務(wù) 250
4.8 grpcurl工具 251
4.8.1 啟動(dòng)反射服務(wù) 251
4.8.2 查看服務(wù)列表 252
4.8.3 服務(wù)的方法列表 253
4.8.4 獲取類型信息 253
4.8.5 調(diào)用方法 254
4.9 補(bǔ)充說明 255
第5章 Go和Web 256
5.1 Web開發(fā)簡(jiǎn)介 256
5.2 請(qǐng)求路由 260
5.2.1 httprouter 260
5.2.2 原理 262
5.2.3 壓縮檢索樹創(chuàng)建過程 263
5.3 中間件 267
5.3.1 代碼泥潭 267
5.3.2 使用中間件剝離非業(yè)務(wù)邏輯 269
5.3.3 更優(yōu)雅的中間件寫法 272
5.3.4 哪些事情適合在中間件中做 273
5.4 請(qǐng)求校驗(yàn) 274
5.4.1 重構(gòu)請(qǐng)求校驗(yàn)函數(shù) 275
5.4.2 用請(qǐng)求校驗(yàn)器解放體力勞動(dòng) 276
5.4.3 原理 277
5.5 Database 和數(shù)據(jù)庫(kù)打交道 279
5.5.1 從database/sql講起 279
5.5.2 提高生產(chǎn)效率的ORM和
SQL Builder 281
5.5.3 脆弱的數(shù)據(jù)庫(kù) 283
5.6 服務(wù)流量限制 285
5.6.1 常見的流量限制手段 287
5.6.2 原理 289
5.6.3 服務(wù)瓶頸和 QoS 291
5.7 常見大型Web項(xiàng)目分層 291
5.8 接口和表驅(qū)動(dòng)開發(fā) 297
5.8.1 業(yè)務(wù)系統(tǒng)的發(fā)展過程 297
5.8.2 使用函數(shù)封裝業(yè)務(wù)流程 298
5.8.3 使用接口來做抽象 298
5.8.4 接口的優(yōu)缺點(diǎn) 301
5.8.5 表驅(qū)動(dòng)開發(fā) 303
5.9 灰度發(fā)布和A/B測(cè)試 303
5.9.1 通過分批次部署實(shí)現(xiàn)灰度發(fā)布 304
5.9.2 通過業(yè)務(wù)規(guī)則進(jìn)行灰度發(fā)布 305
5.9.3 如何實(shí)現(xiàn)一套灰度發(fā)布系統(tǒng) 306
5.10 補(bǔ)充說明 310
第6章 分布式系統(tǒng) 311
6.1 分布式ID生成器 311
6.1.1 worker_id分配 312
6.1.2 開源實(shí)例 313
6.2 分布式鎖 316
6.2.1 進(jìn)程內(nèi)加鎖 317
6.2.2 嘗試鎖 317
6.2.3 基于Redis的setnx 319
6.2.4 基于ZooKeeper 321
6.2.5 基于etcd 321
6.2.6 如何選擇合適的鎖 322
6.3 延時(shí)任務(wù)系統(tǒng) 323
6.3.1 定時(shí)器的實(shí)現(xiàn) 323
6.3.2 任務(wù)分發(fā) 325
6.3.3 數(shù)據(jù)再平衡和冪等考量 326
6.4 分布式搜索引擎 327
6.4.1 搜索引擎 328
6.4.2 異構(gòu)數(shù)據(jù)同步 336
6.5 負(fù)載均衡 337
6.5.1 常見的負(fù)載均衡思路 337
6.5.2 基于洗牌算法的負(fù)載均衡 338
6.5.3 ZooKeeper集群的隨機(jī)節(jié)點(diǎn)挑選問題 340
6.5.4 負(fù)載均衡算法效果驗(yàn)證 340
6.6 分布式配置管理 341
6.6.1 場(chǎng)景舉例 341
6.6.2 使用etcd實(shí)現(xiàn)配置更新 342
6.6.3 配置膨脹 345
6.6.4 配置版本管理 345
6.6.5 客戶端容錯(cuò) 345
6.7 分布式爬蟲 346
6.7.1 基于colly的單機(jī)爬蟲 346
6.7.2 分布式爬蟲 347
6.7.3 結(jié)合nats和colly的消息生產(chǎn) 350
6.7.4 結(jié)合colly的消息消費(fèi) 352
6.8 補(bǔ)充說明 353
附錄A 使用Go語言常遇到的問題 354
附錄B 有趣的代碼片段 363