本書從實踐出發(fā)講解Go語言的進階知識。本書共6章,第1章簡單回顧Go語言的發(fā)展歷史;第2章和第3章系統(tǒng)地介紹CGO編程和Go匯編語言的用法;第4章對RPC和Protobuf技術(shù)進行深入介紹,并講述如何打造一個自己的RPC系統(tǒng);第5章介紹工業(yè)級環(huán)境的Web系統(tǒng)的設(shè)計和相關(guān)技術(shù);第6章介紹Go語言在分布式領(lǐng)域的一些編程技術(shù)。書中還涉及CGO和匯編方面的知識,其中CGO能夠幫助讀者繼承的軟件遺產(chǎn),而在深入學(xué)習(xí)Go運行時,匯編對于理解各種語法設(shè)計的底層實現(xiàn)是必不可少的知識。此外,本書還包含一些緊跟潮流的內(nèi)容,介紹開源界流行的gRPC及其相關(guān)應(yīng)用,講述Go Web框架中的基本實現(xiàn)原理和大型Web項目中的技術(shù)要點,引導(dǎo)讀者對Go語言進行更深入的應(yīng)用。 本書適合對Go語言的應(yīng)用已經(jīng)有一些心得,并希望能夠深入理解底層實現(xiàn)原理或者是希望能夠在Web開發(fā)方面結(jié)合Go語言來實現(xiàn)進階學(xué)習(xí)的技術(shù)人員學(xué)習(xí)和參考。
截至2019年,Go語言已歷經(jīng)10 年,國內(nèi)互聯(lián)網(wǎng)公司的新興項目已經(jīng)在逐漸向Go語言生態(tài)轉(zhuǎn)移。隨著用戶的不斷積累,Go語言相關(guān)教程隨之增加,這些教程主要涵蓋Go語言基礎(chǔ)編程、Web編程、并發(fā)編程和內(nèi)部源碼剖析等諸多內(nèi)容。 本書聚焦于主流Go語言書中缺失的或刻意回避的部分主題,主要面向希望深入了解Go語言,特別是對Go語言和其他語言的混合編程、Go匯編語言的工作機制、構(gòu)造Web框架和分布式開發(fā)等領(lǐng)域感興趣的學(xué)生、工程師和研究人員。閱讀本書需要讀者對Go語言有一定的認識和使用經(jīng)驗。 本書關(guān)于CGO編程和Go匯編語言的講解在中國乃至全球Go語言出版物中是非常有特色的。 本書主要內(nèi)容 ● Go語言演化歷史。 ● CGO編程技術(shù)。 ● Go匯編語言。 ● RPC和gRPC。 ● 構(gòu)造Web框架的方法。 ● 分布式系統(tǒng)。
序一
互聯(lián)網(wǎng)時代的來臨,改變甚至顛覆了很多東西。從前,一臺主機就能搞定一切;而在互聯(lián)網(wǎng)時代,后臺由大量分布式系統(tǒng)構(gòu)成,任何單個后臺服務(wù)器節(jié)點的故障都不會影響整個系統(tǒng)的正常運行。以七牛云、阿里云和騰訊云為代表的云廠商的出現(xiàn)和崛起,標志著云時代的到來。在云時代,掌握分布式編程已經(jīng)成為軟件工程師的基本技能,而基于Go語言構(gòu)建的Docker、Kubernetes等系統(tǒng)正是將云時代推向頂峰的關(guān)鍵力量。
今天,Go語言已歷經(jīng)十年,最初的追隨者也已經(jīng)逐漸成長為Go語言資深用戶。隨著資深用戶的不斷積累,Go語言相關(guān)教程隨之增加,在內(nèi)容層面主要涵蓋Go語言基礎(chǔ)編程、Web編程、并發(fā)編程和內(nèi)部源碼剖析等諸多領(lǐng)域。
本書作者是國內(nèi)第一批Go語言實踐者和Go語言代碼貢獻者,創(chuàng)建了Go語言中國討論組,并組織了早期Go語言相關(guān)中文文檔的翻譯工作。作者從2011年開始分享Go語言和C/C 語言混合編程技術(shù)。本書匯集了作者多年來學(xué)習(xí)和使用Go語言的經(jīng)驗,內(nèi)容涵蓋CGO特性、Go匯編語言、RPC實現(xiàn)、Protobuf插件實現(xiàn)、Web框架實現(xiàn)、分布式系統(tǒng)等高階主題。其中,CGO特性實現(xiàn)了Go語言對C語言和C 語言混合編程的支持,使Go語言可以無縫繼承C/C 世界數(shù)十年來積累的巨大軟件資產(chǎn)。Go匯編語言更是提供了直接調(diào)用底層機器指令的方法,讓我們可以最大限度地提升程序中熱點代碼的性能。
目前,國內(nèi)互聯(lián)網(wǎng)公司的新興項目已經(jīng)在逐漸向Go語言生態(tài)轉(zhuǎn)移,大型分布式系統(tǒng)的開發(fā)實戰(zhàn)經(jīng)驗也是大家關(guān)心的熱點。這些高階或前沿特性正是本書所關(guān)注的課題,在這些方面作者通過不斷鉆研和實踐積累了很多寶貴經(jīng)驗。
總體來說,本書適合有一定Go語言經(jīng)驗,并想深入了解Go語言各種高級用法的開發(fā)人員。對于Go語言新手,建議在閱讀本書前先閱讀一些基礎(chǔ)Go語言編程圖書,例如D&K的The Go Programming Language。
最后,感謝作者在Go語言領(lǐng)域的筆耕不輟和突出貢獻,時代需要的正是這樣對于新興技術(shù)不斷關(guān)注、鉆研和推動的布道者。七牛云作為一家技術(shù)領(lǐng)先的科技公司,也將在這條布道者的道路上不斷前進,為推動科技的發(fā)展、中國企業(yè)的云落地和行業(yè)的數(shù)字化轉(zhuǎn)型貢獻自己的力量。
許式偉,七牛云CEO
2019年5月于上海
柴樹杉,國內(nèi)較早的一批Go語言愛好者,Go語言代碼貢獻者。對WebAssembly技術(shù)有一定研究,除本書外還著有《WebAssembly標準入門》一書。GitHub賬號為chai2010。 曹春暉,在Web 領(lǐng)域工作多年,開源愛好者。對大型網(wǎng)站系統(tǒng)的架構(gòu)和相關(guān)工具的實現(xiàn)很感興趣,并且有一些研究成果。目前在滴滴平臺技術(shù)部工作。
目 錄
第 1章 語言基礎(chǔ) 1
1.1 Go語言創(chuàng)世紀 1
1.1.1 來自貝爾實驗室特有基因 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)者/消費者模型 52
1.6.3 發(fā)布/訂閱模型 53
1.6.4 控制并發(fā)數(shù) 56
1.6.5 贏者為王 57
1.6.6 素數(shù)篩 58
1.6.7 并發(fā)的安全退出 59
1.6.8 context包 62
1.7 錯誤和異常 64
1.7.1 錯誤處理策略 65
1.7.2 獲取錯誤的上下文 67
1.7.3 錯誤的錯誤返回 69
1.7.4 剖析異!70
1.8 補充說明 73
第 2章 CGO編程 74
2.1 快速入門 74
2.1.1 最簡CGO程序 74
2.1.2 基于C標準庫函數(shù)輸出字符串 75
2.1.3 使用自己的C函數(shù) 75
2.1.4 C代碼的模塊化 76
2.1.5 用Go重新實現(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標志條件編譯 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)部機制 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 實戰(zhàn):封裝qsort 103
2.6.1 認識qsort()函數(shù) 103
2.6.2 將qsort()函數(shù)從Go包導(dǎo)出 104
2.6.3 改進:閉包函數(shù)作為比較函數(shù) 106
2.6.4 改進:消除用戶對unsafe包的依賴 108
2.7 CGO內(nèi)存模型 110
2.7.1 Go訪問C內(nèi)存 110
2.7.2 C臨時訪問傳入的Go內(nèi)存 111
2.7.3 C長期持有Go指針對象 113
2.7.4 導(dǎo)出C函數(shù)不能返回Go內(nèi)存 115
2.8 C 類包裝 117
2.8.1 C 類到Go語言對象 117
2.8.2 Go語言對象到C 類 121
2.8.3 徹底解放C 的this指針 125
2.9 靜態(tài)庫和動態(tài)庫 126
2.9.1 使用C靜態(tài)庫 126
2.9.2 使用C動態(tài)庫 128
2.9.3 導(dǎo)出C靜態(tài)庫 129
2.9.4 導(dǎo)出C動態(tài)庫 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 多個非main包中導(dǎo)出C函數(shù) 135
2.11 補充說明 135
第3章 Go匯編語言 136
3.1 快速入門 136
3.1.1 實現(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 沒有分號 142
3.2 計算機結(jié)構(gòu) 142
3.2.1 圖靈機和BrainFuck語言 143
3.2.2 《人力資源機器》游戲 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 標識符規(guī)則和特殊標志 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 高級匯編語言 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è)計沒有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)體對應(yīng)的接口對象 190
3.8.5 goid的應(yīng)用:局部存儲 192
3.9 Delve調(diào)試器 194
3.9.1 Delve入門 194
3.9.2 調(diào)試匯編程序 198
3.10 補充說明 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 自動生成完整的RPC代碼 215
4.3 玩轉(zhuǎn)RPC 218
4.3.1 客戶端RPC的實現(xiàn)原理 218
4.3.2 基于RPC實現(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進階 233
4.5.1 證書認證 233
4.5.2 Token認證 236
4.5.3 截取器 238
4.5.4 和Web服務(wù)共存 240
4.6 gRPC和Protobuf擴展 241
4.6.1 驗證器 241
4.6.2 REST接口 244
4.6.3 Nginx 246
4.7 pbgo:基于Protobuf的框架 246
4.7.1 Protobuf擴展語法 246
4.7.2 插件中讀取擴展信息 248
4.7.3 生成REST代碼 249
4.7.4 啟動REST服務(wù) 250
4.8 grpcurl工具 251
4.8.1 啟動反射服務(wù) 251
4.8.2 查看服務(wù)列表 252
4.8.3 服務(wù)的方法列表 253
4.8.4 獲取類型信息 253
4.8.5 調(diào)用方法 254
4.9 補充說明 255
第5章 Go和Web 256
5.1 Web開發(fā)簡介 256
5.2 請求路由 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 請求校驗 274
5.4.1 重構(gòu)請求校驗函數(shù) 275
5.4.2 用請求校驗器解放體力勞動 276
5.4.3 原理 277
5.5 Database 和數(shù)據(jù)庫打交道 279
5.5.1 從database/sql講起 279
5.5.2 提高生產(chǎn)效率的ORM和
SQL Builder 281
5.5.3 脆弱的數(shù)據(jù)庫 283
5.6 服務(wù)流量限制 285
5.6.1 常見的流量限制手段 287
5.6.2 原理 289
5.6.3 服務(wù)瓶頸和 QoS 291
5.7 常見大型Web項目分層 291
5.8 接口和表驅(qū)動開發(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)缺點 301
5.8.5 表驅(qū)動開發(fā) 303
5.9 灰度發(fā)布和A/B測試 303
5.9.1 通過分批次部署實現(xiàn)灰度發(fā)布 304
5.9.2 通過業(yè)務(wù)規(guī)則進行灰度發(fā)布 305
5.9.3 如何實現(xiàn)一套灰度發(fā)布系統(tǒng) 306
5.10 補充說明 310
第6章 分布式系統(tǒng) 311
6.1 分布式ID生成器 311
6.1.1 worker_id分配 312
6.1.2 開源實例 313
6.2 分布式鎖 316
6.2.1 進程內(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 延時任務(wù)系統(tǒng) 323
6.3.1 定時器的實現(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 負載均衡 337
6.5.1 常見的負載均衡思路 337
6.5.2 基于洗牌算法的負載均衡 338
6.5.3 ZooKeeper集群的隨機節(jié)點挑選問題 340
6.5.4 負載均衡算法效果驗證 340
6.6 分布式配置管理 341
6.6.1 場景舉例 341
6.6.2 使用etcd實現(xiàn)配置更新 342
6.6.3 配置膨脹 345
6.6.4 配置版本管理 345
6.6.5 客戶端容錯 345
6.7 分布式爬蟲 346
6.7.1 基于colly的單機爬蟲 346
6.7.2 分布式爬蟲 347
6.7.3 結(jié)合nats和colly的消息生產(chǎn) 350
6.7.4 結(jié)合colly的消息消費 352
6.8 補充說明 353
附錄A 使用Go語言常遇到的問題 354
附錄B 有趣的代碼片段 363