【後端專場 學習資料一】第五屆位元組跳動青訓營
第五屆位元組跳動青訓營講師非常用心給大家整理了課前、中、後的學習內容,同學們自我評估,選擇性查漏補缺,便於大家更好的跟上講師們的節奏,祝大家學習愉快,多多提問交流~
Go 語言基礎 - 基礎語法
概述
本節課程主要分為四個方面:
- Go 語言簡介
- Go 語言開發入門,包括開發環境配置、基礎語法、標準庫
- Go 實戰,包括三個實戰專案
課前部分主要羅列課程中涉及到的概念。對於不熟悉的概念,同學們可以提前查詢預習;課中部分主要羅列每一部分的關鍵思路,幫助同學們跟上課程的進度;課後部分是一些問題,幫助同學們在課後梳理本課程的重點。
課前 (必須)
安裝 Go 語言
- 訪問 https://go.dev/ ,點選 Download ,下載對應平臺安裝包,安裝即可
- 如果無法訪問上述網址,可以改為訪問 https://studygolang.com/dl 下載安裝
- 如果訪問 github 速度比較慢,建議配置 go mod proxy,參考 https://goproxy.cn/ 裡面的描述配置,下載第三方依賴包的速度可以大大加快
配置 Go 語言開發環境
可以選擇安裝 VS Code , 或者 Goland ,對於 VS Code,需要安裝 Go 外掛
下載課程示例程式碼
- Windows 平臺建議安裝 git,其它系統自帶,安裝教程
- 開啟 https://github.com/wangkechun/go-by-example 克隆課程示例專案
- 進入課程示例專案程式碼目錄,執行
go run example/01-hello/main.go
如果正確輸出 hello world,則說明環境配置正確
【可選】 學習 Go 語言基礎語法
空餘時間閱讀 Go語言聖經(中文版)
課後
Go 語言進階 - 工程進階
概述
本節課程主要分為四個方面:
- 併發程式設計
- 依賴管理
- 單元測試
- 專案實戰
詳述
- 羅列課程中涉及到的概念和相關資料,對於不熟悉的知識點,希望同學們可以提前查詢預習,屆時跟上直播課程進度。
- 【必須】課程內容相關程式碼連結:https://github.com/Moonlight-Zhao/go-project-example/tree/V0
併發程式設計
- 協程Goroutine
- 通道Channel
- 鎖Lock https://pkg.go.dev/sync
- 執行緒同步WaitGroup https://pkg.go.dev/sync
屬於程式設計進階內容,考慮到工程專案的可用性和可靠性,工程實踐中經常會用到。
依賴管理
- Gopath
- Go Vendor
- Go Module : https://go.dev/blog/using-go-modules
瞭解Go依賴管理演進的歷程,通過課程學習以及課後實踐能能夠熟練使用go module 管理依賴。
單元測試
- 單元測試概念和規則:https://go.dev/doc/tutorial/add-a-test;https://pkg.go.dev/testing
- Mock測試:https://github.com/bouk/monkey
- 基準測試:https://pkg.go.dev/testing#hdr-Benchmarks
專案實戰
需求模型來源
青訓營話題頁https://forum.juejin.cn/youthcamp/post/7081211487762513928?from=1
需求
- 實現一個展示話題(標題,文字描述)和回帖列表的後端http介面;
- 本地檔案儲存資料
元件及技術點
-
web框架:Gin - https://github.com/gin-gonic/gin#quick-start
- 瞭解go web框架的簡單使用
-
分層結構設計:https://github.com/bxcodec/go-clean-arch
- 瞭解分層設計的概念
-
檔案操作:讀檔案https://pkg.go.dev/io
-
資料查詢:索引https://www.baike.com/wikiid/5527083834876297305?prd=result_list&view_id=5di0ak8h3ag000
課後實踐
- 支援對話題釋出回帖。
- 回帖id生成需要保證不重複、唯一性。
- 新加回帖追加到本地檔案,同時需要更新索引,注意Map的併發安全問題 。
Go 框架三件套詳解(Web/RPC/ORM)
環境搭建部分
搭建課程所需要的開發環境以及安裝需要用到的軟體。
學習如何安裝 Docker/Postman/Git/Golang
-
安裝 Minikube 或 Docker Desktop 用於使用 Docker 安裝教程
- 可以使用 Minikube 或者使用 Docker Desktop 啟動 Docker
-
安裝 Postman
-
安裝 Git 安裝教程
-
安裝 Go(Golang >= 1.15) 安裝教程
框架體驗部分
提前體驗一下課程涉及的 HTTP/RPC/ORM 框架
HTTP 框架 Hertz 初體驗
通過閱讀 https://www.cloudwego.io/zh/docs/hertz/getting-started/ 嘗試執行 Hertz 的示例程式碼
- Hertz 框架地址: https://github.com/cloudwego/hertz
RPC 框架 Kitex 初體驗
通過閱讀 https://www.cloudwego.io/zh/docs/kitex/getting-started/ 嘗試執行 Kitex 的示例程式碼
- kitex 暫時沒有針對 Windows 做支援,如果本地開發環境是 Windows 建議使用 WSL2
- KItex 框架地址: https://github.com/cloudwego/kitex/
ORM 框架 Gorm 初體驗
通過閱讀 https://gorm.cn/docs/#Install 嘗試執行 Gorm 的示例程式碼
- Gorm 框架地址: https://github.com/go-gorm/gorm
其它知識
瞭解一下什麼IDL以及IDL的語法
瞭解一下什麼是 opentracing 以及 etcd
Etcd 與 Opentracing 是什麼
IDL 是什麼
-
瞭解 IDL 是什麼 https://zh.m.wikipedia.org/zh-hans/%E6%8E%A5%E5%8F%A3%E6%8F%8F%E8%BF%B0%E8%AF%AD%E8%A8%80
-
Thrift IDL 語法 https://thrift.apache.org/docs/idl
-
proto3 IDL 語法 https://developers.google.com/protocol-buffers/docs/proto3
高質量程式設計與效能調優實戰
課程概述
- 介紹編碼規範,幫助大家寫出高質量程式
- 介紹 Go 語言的效能優化建議,分析對比不同方式對效能的影響和背後的原理
- 講解常用效能分析工具 pprof 的使用和工作原理,熟悉排查程式效能問題的基本流程
- 分析效能調優實際案例,介紹實際效能調優時的工作內容
課前
- 課程內容概要
實踐準備 (必須)
- 克隆 https://github.com/wolfogre/go-pprof-practice 到本地,保證能夠編譯執行
- 嘗試使用 test 命令,編寫並執行簡單測試 https://go.dev/doc/tutorial/add-a-test
- 嘗試使用 -bench 引數,對編寫的函式進行效能測試,https://pkg.go.dev/testing#hdr-Benchmarks
推薦閱讀
- Go 程式碼 Review 建議https://github.com/golang/go/wiki/CodeReviewComments
- Uber 的 Go 編碼規範,https://github.com/uber-go/guide
課中
高質量程式設計
簡介
- 編寫的程式碼能夠達到正確可靠、簡潔清晰、無效能隱患的目標就能稱之為高質量程式碼
- 實際應用場景千變萬化,各種語言的特性和語法各不相同,但是高質量程式設計遵循的原則是相通的
- 高質量的程式設計需要注意以下原則:簡單性、可讀性、生產力
常見編碼規範
程式碼格式
- 使用 gofmt 自動格式化程式碼,保證所有的 Go 程式碼與官方推薦格式保持一致
總結
- 提升可讀性,風格一致的程式碼更容易維護、需要更少的學習成本、團隊合作成本,同時可以降低 Review 成本
註釋
-
註釋應該解釋程式碼作用
- 適合註釋公共符號,https://github.com/golang/go/blob/master/src/os/file.go#L313
-
註釋應該解釋程式碼如何做的
- 適合註釋方法,https://github.com/golang/go/blob/master/src/net/http/client.go#L678
-
註釋應該解釋程式碼實現的原因
- 解釋程式碼的外部因素,https://github.com/golang/go/blob/master/src/net/http/client.go#L521
- 註釋應該解釋程式碼什麼情況會出錯
-
公共符號始終要註釋
- 包中宣告的每個公共的符號:變數、常量、函式以及結構都需要添加註釋
- https://github.com/golang/go/blob/master/src/io/io.go#L638
- https://github.com/golang/go/blob/master/src/io/io.go#L455
總結
- 程式碼是最好的註釋
- 註釋應該提供程式碼未表達出的上下文資訊
命名規範
-
variable
- 簡潔勝於冗長
- 縮略詞全大寫,但當其位於變數開頭且不需要匯出時,使用全小寫
- 變數距離其被使用的地方越遠,則需要攜帶越多的上下文資訊
- 全域性變數在其名字中需要更多的上下文資訊,使得在不同地方可以輕易辨認出其含義
-
function
- 函式名不攜帶包名的上下文資訊,因為包名和函式名總是成對出現的
- 函式名儘量簡短
- 當名為 foo 的包某個函式返回型別 Foo 時,可以省略型別資訊而不導致歧義
- 當名為 foo 的包某個函式返回型別 T 時(T 並不是 Foo),可以在函式名中加入型別資訊
-
package
- 只由小寫字母組成。不包含大寫字母和下劃線等字元
- 簡短幷包含一定的上下文資訊。例如 schema、task 等
- 不要與標準庫同名。例如不要使用 sync 或者 strings
總結
- 關於命名的大多數規範核心在於考慮上下文
- 人們在閱讀理解程式碼的時候也可以看成是計算機執行程式,好的命名能讓人把關注點留在主流程上,清晰地理解程式的功能,避免頻繁切換到分支細節,增加理解成本
控制流程
- 避免巢狀,保持正常流程清晰
- 如果兩個分支中都包含 return 語句,則可以去除冗餘的 else
-
儘量保持正常程式碼路徑為最小縮排,優先處理錯誤情況/特殊情況,並儘早返回或繼續迴圈來減少巢狀,增加可讀性
- Go 公共庫的程式碼
- https://github.com/golang/go/blob/master/src/bufio/bufio.go#L277
總結
- 線性原理,處理邏輯儘量走直線,避免複雜的巢狀分支
- 提高程式碼的可讀性
錯誤和異常處理
-
簡單錯誤處理
- 優先使用 errors.New 來建立匿名變數來直接表示該錯誤。有格式化需求時使用 fmt.Errorf
- https://github.com/golang/go/blob/master/src/net/http/client.go#L802
-
錯誤的 Wrap 和 Unwrap
- 在 fmt.Errorf 中使用 %w 關鍵字來將一個錯誤 wrap 至其錯誤鏈中
- https://github.com/golang/go/blob/master/src/cmd/go/internal/work/exec.go#L983
-
Go1.13 在 errors 中新增了三個新 API 和一個新的 format 關鍵字,分別是 errors.Is、errors.As 、errors.Unwrap 以及 fmt.Errorf 的 %w。如果專案執行在小於 Go1.13 的版本中,匯入 golang.org/x/xerrors 來使用。以下語法均已 Go1.13 作為標準。
-
錯誤判定
- 使用 errors.Is 可以判定錯誤鏈上的所有錯誤是否含有特定的錯誤。
- https://github.com/golang/go/blob/master/src/cmd/go/internal/modfetch/sumdb.go#L208
- 在錯誤鏈上獲取特定種類的錯誤,使用 errors.As
- https://github.com/golang/go/blob/master/src/errors/wrap_test.go#L255
-
panic
- 不建議在業務程式碼中使用 panic
- 如果當前 goroutine 中所有 deferred 函式都不包含 recover 就會造成整個程式崩潰
- 當程式啟動階段發生不可逆轉的錯誤時,可以在 init 或 main 函式中使用 panic
- https://github.com/Shopify/sarama/blob/main/examples/consumergroup/main.go#L94
-
recover
- recover 只能在被 defer 的函式中使用,巢狀無法生效,只在當前 goroutine 生效
- https://github.com/golang/go/blob/master/src/fmt/scan.go#L247
- 如果需要更多的上下文資訊,可以 recover 後在 log 中記錄當前的呼叫棧。
- https://github.com/golang/website/blob/master/internal/gitfs/fs.go#L228
總結
- panic 用於真正異常的情況
- error 儘可能提供簡明的上下文資訊,方便定位問題
- recover 生效範圍,在當前 goroutine 的被 defer 的函式中生效
效能優化建議
- 在滿足正確性、可靠性、健壯性、可讀性等質量因素的前提下,設法提高程式的效率
- 效能對比測試程式碼,可參考 https://github.com/RaymondCode/go-practice
-
slice 預分配記憶體
-
在儘可能的情況下,在使用 make() 初始化切片時提供容量資訊,特別是在追加切片時
-
原理
-
https://ueokande.github.io/go-slice-tricks/
-
切片本質是一個數組片段的描述,包括了陣列的指標,這個片段的長度和容量(不改變記憶體分配情況下的最大長度)
-
切片操作並不複製切片指向的元素,建立一個新的切片會複用原來切片的底層陣列,因此切片操作是非常高效的
-
切片有三個屬性,指標(ptr)、長度(len) 和容量(cap)。append 時有兩種場景:
- 當 append 之後的長度小於等於 cap,將會直接利用原底層陣列剩餘的空間
- 當 append 後的長度大於 cap 時,則會分配一塊更大的區域來容納新的底層陣列
-
因此,為了避免記憶體發生拷貝,如果能夠知道最終的切片的大小,預先設定 cap 的值能夠獲得最好的效能
-
-
另一個陷阱:大記憶體得不到釋放
- 在已有切片的基礎上進行切片,不會建立新的底層陣列。因為原來的底層陣列沒有發生變化,記憶體會一直佔用,直到沒有變數引用該陣列
- 因此很可能出現這麼一種情況,原切片由大量的元素構成,但是我們在原切片的基礎上切片,雖然只使用了很小一段,但底層陣列在記憶體中仍然佔據了大量空間,得不到釋放
- 推薦的做法,使用 copy 替代 re-slice
-
-
map 預分配記憶體
-
原理
- 不斷向 map 中新增元素的操作會觸發 map 的擴容
- 根據實際需求提前預估好需要的空間
- 提前分配好空間可以減少記憶體拷貝和 Rehash 的消耗
-
-
使用 strings.Builder
-
常見的字串拼接方式
- +
- strings.Builder
- bytes.Buffer
-
strings.Builder 最快,bytes.Buffer 較快,+ 最慢
-
原理
- 字串在 Go 語言中是不可變型別,佔用記憶體大小是固定的,當使用 + 拼接 2 個字串時,生成一個新的字串,那麼就需要開闢一段新的空間,新空間的大小是原來兩個字串的大小之和
- strings.Builder,bytes.Buffer 的記憶體是以倍數申請的
- strings.Builder 和 bytes.Buffer 底層都是 []byte 陣列,bytes.Buffer 轉化為字串時重新申請了一塊空間,存放生成的字串變數,而 strings.Builder 直接將底層的 []byte 轉換成了字串型別返回
-
-
使用空結構體節省記憶體
-
空結構體不佔據記憶體空間,可作為佔位符使用
-
比如實現簡單的 Set
- Go 語言標準庫沒有提供 Set 的實現,通常使用 map 來代替。對於集合場景,只需要用到 map 的鍵而不需要值
-
-
使用 atomic 包
-
原理
- 鎖的實現是通過作業系統來實現,屬於系統呼叫,atomic 操作是通過硬體實現的,效率比鎖高很多
- sync.Mutex 應該用來保護一段邏輯,不僅僅用於保護一個變數
- 對於非數值系列,可以使用 atomic.Value,atomic.Value 能承載一個 interface{}
-
總結
- 避免常見的效能陷阱可以保證大部分程式的效能
- 針對普通應用程式碼,不要一味地追求程式的效能,應當在滿足正確可靠、簡潔清晰等質量要求的前提下提高程式效能
效能調優實戰
效能調優簡介
-
效能調優原則
- 要依靠資料不是猜測
- 要定位最大瓶頸而不是細枝末節
- 不要過早優化
- 不要過度優化
效能分析工具
效能調優的核心是效能瓶頸的分析,對於 Go 應用程式,最方便的就是 pprof 工具
-
pprof 功能說明
- pprof 是用於視覺化和分析效能分析資料的工具
- 可以知道應用在什麼地方耗費了多少 CPU、memory 等執行指標
-
pprof 實踐
-
https://github.com/wolfogre/go-pprof-practice
-
前置準備,熟悉簡單指標,能夠編譯執行 pprof 測試專案
-
實際分析排查過程
-
排查 CPU 問題
-
命令列分析
- go tool pprof "http://localhost:6060/debug/pprof/profile?seconds=10"
-
top 命令
-
list 命令
-
熟悉 web 頁面分析
-
呼叫關係圖,火焰圖
-
go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/cpu"
-
-
排查堆記憶體問題
- go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/heap"
-
排查協程問題
- go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/goroutine"
-
排查鎖問題
- go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/mutex"
-
排查阻塞問題
- go tool pprof -http=:8080 "http://localhost:6060/debug/pprof/block"
-
-
-
pprof 的取樣過程和原理
- CPU 取樣
- 堆記憶體取樣
- 協程和系統執行緒取樣
- 阻塞操作和鎖競爭取樣
效能調優案例
-
基本概念
- 服務:能單獨部署,承載一定功能的程式
- 依賴:Service A 的功能實現依賴 Service B 的響應結果,稱為 Service A 依賴 Service B
- 呼叫鏈路:能支援一個介面請求的相關服務集合及其相互之間的依賴關係
- 基礎庫:公共的工具包、中介軟體
-
業務優化
-
流程
- 建立服務效能評估手段
- 分析效能資料,定位效能瓶頸
- 重點優化項改造
- 優化效果驗證
-
建立壓測評估鏈路
- 服務效能評估
- 構造請求流量
- 壓測範圍
- 效能資料採集
-
分析效能火焰圖,定位效能瓶頸
- pprof 火焰圖
-
重點優化項分析
- 規範元件庫使用
- 高併發場景優化
- 增加程式碼檢查規則避免增量劣化出現
- 優化正確性驗證
-
上線驗證評估
- 逐步放量,避免出現問題
-
進一步優化,服務整體鏈路分析
- 規範上游服務呼叫介面,明確場景需求
- 分析業務流程,通過業務流程優化提升服務效能
-
-
基礎庫優化
-
適應範圍更廣,覆蓋更多服務
-
AB 實驗 SDK 的優化
- 分析基礎庫核心邏輯和效能瓶頸
- 完善改造方案,按需獲取,序列化協議優化
- 內部壓測驗證
- 推廣業務服務落地驗證
-
-
Go 語言優化
-
適應範圍最廣,Go 服務都有收益
-
優化方式
- 優化記憶體分配策略
- 優化程式碼編譯流程,生成更高效的程式
- 內部壓測驗證
- 推廣業務服務落地驗證
-
課後
- 瞭解下其他語言的編碼規範,是否和 Go 語言編碼規範有相通之處,注重理解哪些共同點
- 編碼規範或者效能優化建議大部分是通用的,有沒有方式能夠自動化對程式碼進行檢測?
- 從 https://github.com/golang/go/tree/master/src 中選擇感興趣的包,看看官方程式碼是如何編寫的
- 使用 Go 進行併發程式設計時有哪些效能陷阱或者優化手段?
- 在真實的線上環境中,每個場景或者服務遇到的效能問題也是各種各樣,搜尋下知名公司的官方公眾號或者部落格,裡面有哪些效能優化的案例?比如 https://eng.uber.com/category/oss-projects/oss-go/
- Go 語言本身在持續更新迭代,每個版本在效能上有哪些重要的優化點?
參考資料
- 熟悉 Go 語言基礎後的必讀內容,https://go.dev/doc/effective_go
- Dave Cheney 關於 Go 語言程式設計實踐的演講記錄,https://dave.cheney.net/practical-go/presentations/qcon-china.html
- 《程式設計的原則:改善程式碼質量的101個方法》,總結了很多程式設計原則,按照是什麼 -> 為什麼 -> 怎麼做進行了說明,https://mp.weixin.qq.com/s/vXSZOl2Gt7wcgq1OL9Cwow
- 如何編寫整潔的 Go 程式碼,https://github.com/Pungyeon/clean-go-article
- Go 官方部落格,有關於 Go 的最新進展,https://go.dev/blog/
- Dave Cheney 關於 Go 語言程式設計高效能程式設計的介紹,https://dave.cheney.net/high-performance-go-workshop/dotgo-paris.html
- Go 語言高效能程式設計,博主總結了 Go 程式設計的一些效能建議, https://geektutu.com/post/high-performance-go.html
- Google 其他程式語言編碼規範,可以對照參考,https://zh-google-styleguide.readthedocs.io/en/latest/
- 【前端專場 學習資料三】第五屆位元組跳動青訓營
- 【後端專場 學習資料一】第五屆位元組跳動青訓營
- 【前端專場 學習資料一】第五屆位元組跳動青訓營
- 【伴學青訓營】閱讀打卡活動來襲!
- 第五屆位元組跳動青訓營來襲,這個寒假來點不一樣的!
- 叮咚!青訓營 - 快樂出發 Flag 打卡活動來啦
- 第四屆青訓營閱讀打卡活動來啦,獎品、規則全面升級,快來學習吧
- 【中介軟體 學習資料】第三屆位元組跳動青訓營 - 後端專場
- 【Go 語言框架與實現 學習資料】第三屆位元組跳動青訓營 - 後端專場
- 【Go 語言原理與實踐學習資料(下)】第三屆位元組跳動青訓營-後端專場
- 青訓營21天閱讀打卡活動,堅持就拿大禮包
- 【第三屆位元組跳動青訓營|刷題打卡】DAY7
- 【第三屆位元組跳動青訓營|刷題打卡】DAY4