【Go 語言框架與實現 學習資料】第三屆位元組跳動青訓營 - 後端專場
theme: juejin
第三屆位元組跳動青訓營講師非常用心給大家整理了課前、中、後的學習內容,同學們自我評估,選擇性查漏補缺,便於大家更好的跟上講師們的節奏,祝大家學習愉快,多多提問交流~
第十三節:深入淺出 RPC 框架
概述
本節課程主要分為四個方面:
- RPC 相關的基本概念
- RPC 框架的分層設計
- 衡量 RPC 框架的一些核心指標
- 位元組內部 RPC 框架 Kitex 實踐分享
課前部分主要羅列課程中涉及到的概念。對於不熟悉的概念,同學們可以提前查詢預習;
課中部分主要羅列每一部分的關鍵思路,幫助同學們跟上課程的進度;
課後部分是一些問題,幫助同學們在課後梳理本課程的重點。
課前
RPC 的基本概念
-
RPC的概念模型:User、User-Stub、RPC-Runtime、Server-Stub、Server
-
IDL(Interface Definition Language) 檔案
- Thrift
- Protobuf
- 生成程式碼
- 編解碼(序列化/反序列化)
-
通訊協議
- 應用層協議
-
網路通訊
-
IO 網路模型
- blocking IO
- unblocking IO
- IO multiplexing
- signal driven IO
- asynchronous IO
-
傳輸層協議
- TCP
- UDP
-
RPC 框架分層設計
-
編解碼層
-
資料格式:
-
語言特定格式
-
文字格式
-
二進位制編碼
- TLV 編碼:Thrift 使用 TLV 編碼
- Varint 編碼:Protobuf 使用 Varint 編碼
-
-
選項:
- 相容性
- 通用型
- 效能
-
-
傳輸協議層
-
訊息切分
- 特殊結束符
- 變長協議:length+body
-
協議構造
- 以 Thrift 的 THeader 協議為例講解
-
-
網路通訊層
-
網路庫
-
核心指標
- 吞吐高
- 延遲低
-
RPC 框架的核心指標
-
穩定性
-
保障策略
- 熔斷
- 限流
- 超時
-
請求成功率
- 負載均衡
- 重試
-
長尾請求
- BackupRequest
-
-
易用性
- 開箱即用
- 周邊工具
- 擴充套件性
-
觀測性
- Log
- Metric
- Tracing
- 內建觀測性服務
- 高效能
位元組內部 Kitex 實踐分享
- Kitex 整體架構
- 自研網路庫 Netpoll
-
效能優化:
- 網路庫優化
- 編解碼優化
- 合併部署
課中
基本概念
-
相比本地函式呼叫,RPC呼叫需要解決的問題
- 函式對映
- 資料轉換成位元組流
- 網路傳輸
- 一次 RPC 的完整過程
-
RPC 帶來的問題將由 RPC 框架來解決
- 服務宕機如何感知?
- 遇到網路異常應該如何應對?
- 請求量暴增怎麼處理?
RPC 框架分層設計
編解碼層
-
資料格式
- 語言特定格式:例如 java.io.Serializable
- 文字格式:例如 JSON、XML、CSV 等
- 二進位制編碼:常見有 Thrift 的 BinaryProtocol,Protobuf,實現可以有多種形式,例如 TLV 編碼 和 Varint 編碼
-
選型考察點
-
相容性
-
通用型
-
- 空間開銷
- 時間開銷
-
- 生成程式碼和編解碼層相互依賴,框架的編解碼應當具備擴充套件任意編解碼協議的能力
協議層
-
以 Thrift 的 THeader 協議為例
- LENGTH 欄位 32bits,包括資料包剩餘部分的位元組大小,不包含 LENGTH 自身長度 - HEADER MAGIC 欄位16bits,值為:0x1000,用於標識 協議版本資訊,協議解析的時候可以快速校驗 - FLAGS 欄位 16bits,為預留欄位,暫未使用,預設值為 0x0000 - SEQUENCE NUMBER 欄位 32bits,表示資料包的 seqId,可用於多路複用,最好確保單個連線內遞增 - HEADER SIZE 欄位 16bits,等於頭部長度位元組數/4,頭部長度計算從第14個位元組開始計算,一直到 PAYLOAD 前(備註:header 的最大長度為 64K) - PROTOCOL ID 欄位 uint8 編碼,取值有: - ProtocolIDBinary = 0 - ProtocolIDCompact = 2 - NUM TRANSFORMS 欄位 uint8 編碼,表示 TRANSFORM 個數 - TRANSFORM ID 欄位 uint8 編碼,表示壓縮方式 zlib or snappy - INFO ID 欄位 uint8 編碼,具體取值參考下文,用於傳遞一些定製的 meta 資訊 - PAYLOAD 訊息內容
- 協議解析
網路通訊層
- 阻塞 IO 下,耗費一個執行緒去阻塞在 read(fd) 去等待用足夠多的資料可讀並返回。
- 非阻塞 IO 下,不停對所有 fds 輪詢 read(fd) ,如果讀取到 n <= 0 則下一個迴圈繼續輪詢。
第一種方式浪費執行緒(會佔用記憶體和上下文切換開銷),第二種方式浪費 CPU 做大量無效工作。而基於 IO 多路複用系統呼叫實現的 Poll 的意義在於將可讀/可寫狀態通知和實際檔案操作分開,並支援多個檔案描述符通過一個系統呼叫監聽以提升效能。
網路庫的核心功能就是去同時監聽大量的檔案描述符的狀態變化(通過作業系統呼叫),並對於不同狀態變更,高效,安全地進行對應的檔案操作。
RPC 框架核心指標
穩定性
-
保障策略
- 熔斷
- 限流
- 超時控制
從某種程度上講超時、限流和熔斷也是一種服務降級的手段 。
-
請求成功率
- 負載均衡
- 重試
-
長尾請求
- BackupRequest
易用性
-
開箱即用
- 合理的預設引數選項、豐富的文件
-
周邊工具
- 生成程式碼工具、腳手架工具
擴充套件性
- Middleware:middleware 會被構造成一個有序呼叫鏈逐個執行,比如服務發現、路由、負載均衡、超時控制等
- Option:作為初始化引數
- 核心層是支援擴充套件的:編解碼、協議、網路傳輸層
- 程式碼生成工具也支援外掛擴充套件
觀測性
- 三件套:Log、Metric 和 Tracing
-
內建觀測性服務,用於觀察框架內部狀態
- 當前環境變數
- 配置引數
- 快取資訊
- 內建 pprof 服務用於排查問題
高效能
- 連線池和多路複用:複用連線,減少頻繁建聯帶來的開銷
- 高效能編解碼協議:Thrift、Protobuf、Flatbuffer 和 Cap'n Proto 等
- 高效能網路庫:Netpoll 和 Netty 等
位元組內部 Kitex 實踐分享
- 框架文件 Kitex
-
自研網路庫 Netpoll,背景:
a. 原生庫無法感知連線狀態
b. 原生庫存在 goroutine 暴漲的風險
- 擴充套件性:支援多協議,也支援靈活的自定義協議擴充套件
-
效能優化,參考 位元組跳動 Go RPC 框架 KiteX 效能優化實踐
a. 網路優化
i. 排程優化 ii. LinkBuffer 減少記憶體拷貝,從而減少 GC iii. 引入記憶體池和物件池
b. 編解碼優化
i. Codegen:預計算提前分配記憶體,inline,SIMD等 ii. JIT:無生產程式碼,將編譯過程移到了程式的載入(或首次解析)階段,可以一次性編譯生成對應的 codec 並高效執行
-
合併部署
a. 微服務過微,引入的額外的傳輸和序列化開銷越來越大
b. 將強依賴的服務統計部署,有效減少資源消耗
課後
- 行業內各個流行的 RPC 框架的優劣對比
- 從第三章節 RPC 的核心指標來看,Kitex 還有哪些功能是欠缺或者需要加強的?
- 瞭解微服務的新趨勢 ServiceMesh,以及 RPC 框架和 ServiceMesh 的關係
- 關於 RPC 框架,業界有哪些新的趨勢和概念?
- Netpoll 的優勢在哪?相比其他高效能網路庫例如 Netty 還有什麼不足?
- Flatbuffer 和 Cap'n Proto 等編解碼協議為什麼高效能?
參考文獻
第十四節:HTTP 框架修煉之道
概述
本節課程主要分為四個方面:
- HTTP 協議相關知識
- HTTP 框架的設計與實現
- HTTP 框架的優化手段
- 企業實踐
課前部分主要羅列課程中涉及到的概念。對於不熟悉的概念,同學們可以提前查詢預習;課後部分是一些問題,幫助同學們在課後梳理本課程的重點。
課前
HTTP 協議
- HTTP 協議出現背景
- HTTP 協議是什麼
- HTTP 協議有什麼
可參考百度百科
嘗試寫一個 hello world 伺服器
可嘗試用 gin 寫一個 hello world 程式,達到以下效果
HTTP 框架中常見概念
-
框架路由:根據請求的 URI 選擇對應的處理函式。
-
首先匹配 HTTP 方法
-
靜態路由: 精確匹配註冊的路由,如:/a/b/c、/a/b/d
-
引數路由:
- 命名引數:形如
:name
這類叫做命名引數,命名引數只匹配單個路徑段: -
``` Pattern: /user/:user
/user/gordon match(user = gordon) /user/you match(user = you) /user/gordon/profile no match /user/ no match
- 通配引數:形如 **`*action`**這類叫做通配引數,就像名字所暗示的那樣,它們匹配所有內容。因此,它們必須始終位於模式的末尾: -
Pattern: /src/*filepath/src/ match(filepath = "") /src/somefile.go match(filepath = somefile.go) /src/subdir/somefile.go match(filepath = subdie/somefile.go) ```
- 命名引數:形如
-
路由修復: 如果只註冊了 /a/b,但是訪問的 URI 是 /a/b/,那可以提供自動重定向到 /a/b 能力;同樣,如果只註冊了 /a/b/,但是訪問的 URI 是 /a/b,那可以提供自動重定向到 /a/b/ 能力
-
衝突路由:同時註冊 /a/b 和 /:id/b,並設定優先順序。比如:當請求 URI 為 /a/b 時,優先匹配靜態路由 /a/b
-
Golang
- sync.Pool 用法
網路庫
- C10K Problem
- Select,Poll,Epoll
- Epoll ET、LT 區別
- 位元組跳動自研網路庫 netpoll,netpoll-examples
SIMD
- SIMD 是什麼,可參考維基百科
課後作業
- 為什麼 HTTP 框架做要分層設計?分層設計有哪些優勢與劣勢。
- 現有開源社群 HTTP 框架有哪些優勢與不足。
- 中介軟體還有沒有其他實現方式?可以用虛擬碼說明。
- 完成基於字首路由樹的註冊與查詢功能?可以用虛擬碼說明。
- 路由還有沒有其他的實現方式?
第十五節:微服務架構原理與治理實踐
概述
本課程內容主要分為以下4個方面:
-
微服務架構介紹
- 微服務架構的背景由來、架構概覽、基本要素
-
微服務架構原理及特徵
- 微服務架構的基本元件、工作原理、流量特徵
-
核心服務治理功能
- 核心的服務治理功能,包括流量治理、服務均衡、穩定性治理
-
位元組跳動服務治理實踐
- 位元組跳動在微服務架構穩定性治理中,對請求重試策略的探索及實踐
為了幫助大家更好地預習及理解本節課程,該學員手冊列出了課前、課中、及課後這三個階段所涉及到的專業內容大綱,其中課前部分供同學們提前預習參考,課中部分給出了課程大綱,幫助同學們整理思路,課後部分列出一些擴充套件性的問題讓同學們進一步延伸思考。
課前
微服務架構介紹
-
系統架構的演進歷史
- 單體架構
- 垂直應用架構
- 分散式架構
- SOA架構
- 微服務架構
-
微服務架構的三大要素
- 服務治理
- 可觀測性
- 安全
微服務架構原理及特徵
-
微服務架構中的基本概念及元件
- 服務、例項......
-
服務間通訊
- RPC、HTTP
- 服務註冊及服務發現
核心服務治理功能
-
服務釋出
- 藍綠部署
- 灰度釋出(金絲雀釋出)
- 流量治理
-
負載均衡
- Round Robin
- Ring Hash
- Random
-
穩定性治理
- 限流
- 熔斷
- 過載保護
- 降級
位元組跳動服務治理實踐
- 請求重試的意義
- 請求重試的難點
課中
微服務架構介紹
系統架構的演進歷史
-
單體架構
- All in one process
-
垂直應用架構
- 按照業務線垂直劃分
-
分散式架構
- 抽出與業務無關的公共模組
-
SOA架構
- 面向服務
-
微服務架構
- 徹底的服務化
微服務架構概覽
- 閘道器
- 服務配置和治理
- 鏈路追蹤和監控
微服務架構的三大要素
-
服務治理(本課程內容)
- 服務註冊
- 服務發現
- 負載均衡
- 擴縮容
- 流量治理
- 穩定性治理
-
可觀測性
- 日誌採集
- 日誌分析
- 監控打點
- 監控大盤
- 異常報警
- 鏈路追蹤
-
安全
- 身份驗證
- 認證授權
- 訪問令牌
- 審計
- 傳輸加密
- 黑產攻擊
微服務架構原理及特徵
微服務架構中的基本概念及元件
-
服務
- 一組具有相同邏輯的執行實體
-
例項
- 一個服務中的每個執行實體
-
例項與程序的關係
- 沒有必然對應關係,一般一對一或者一對多
-
常見的例項承載形式
- 程序、VM、k8s pod......
服務間通訊
- 微服務之間通過網路進行通訊
- 常見的通訊協議包括 HTTP、RPC
服務註冊及服務發現
-
基本問題
- 服務間呼叫中,如何指定下游服務例項的地址?
-
簡單方案
-
直接指定 ip:port?
- 沒有任何動態能力
- 有多個例項下游例項怎麼辦?
-
使用 DNS?
- 本地 DNS 存在快取,導致延遲
- DNS 沒有負載均衡
- 不支援服務探活檢查
- DNS 不能指定埠
-
-
服務註冊發現
- 新增一個統一的服務註冊中心,用於儲存服務名到服務例項之間的對映關係
- 舊服務例項下線前,從服務註冊中心刪除該例項,下線流量
- 新服務例項上線後,在服務註冊中心註冊該例項,上線流量
-
微服務流量特徵
- 統一閘道器入口
- 外網通訊多數採用 HTTP,內網通訊多數採用 RPC(Thrift, gRPC)
核心服務治理功能
服務釋出
-
何為服務釋出
- 讓一個服務升級執行新的程式碼的過程
-
服務釋出難點
- 服務不可用
- 服務抖動
- 服務回滾
-
藍綠部署
- 將服務分成兩個部分,分別先後釋出
- 簡單、穩定
- 但需要兩倍資源
-
灰度釋出(金絲雀釋出)
- 先發布少部分例項,接著逐步增加發布比例
- 不需要增加資源
- 回滾難度大,基礎設施要求高
流量治理
-
流量控制
- 在微服務架構中,可以從各個維度對端到端的流量在鏈路上進行精確控制
-
控制維度
- 地區維度
- 叢集維度
- 例項維度
- 請求維度
負載均衡
- Round Robin
- Random
- Ring Hash
- Least Request
穩定性治理
-
限流
- 限制服務處理的最大 QPS,拒絕過多請求
-
熔斷
- 中斷請求路徑,增加冷卻時間從而讓故障例項嘗試恢復
-
過載保護
- 在負載高的例項中,主動拒絕一部分請求,防止例項被打掛
-
降級
- 服務處理能力不足時,拒絕低級別的請求,只響應線上高優請求
位元組跳動服務治理實踐
-
請求重試的意義
-
本地函式呼叫
- 通常沒有重試意義
-
遠端函式呼叫
- 網路抖動、下游負載高、下游機器宕機......
- 重試是有意義的,可以避免偶發性的錯誤,提高 SLA
-
重試的意義
- 降低錯誤率
- 降低長尾延時
- 容忍暫時性錯誤
- 避開下游故障例項
-
-
請求重試的難點
-
冪等性
- POST 請求可以重試嗎?
-
重試風暴
- 隨著呼叫鏈路的增加,重試次數呈指數級上升
-
超時設定
- 假設呼叫時間一共1s,經過多少時間開始重試?
-
-
重試策略
-
限制重試比例
- 設定一個重試比例閾值(例如 1%),重試次數佔所有請求比例不超過該閾值
-
防止鏈路重試
- 返回特殊的 status code,表示“請求失敗,但別重試”
-
Hedged Requests
- 對於可能超時(或延時高)的請求,重新向另一個下游例項傳送一個相同的請求,並等待先到達的響應
-
-
重試效果驗證
- 位元組跳動重試元件能夠極大限制重試發生的鏈路放大效應
課後
- 結合 CAP 等原理,思考微服務架構有哪些缺陷?
- 微服務是否拆分得越“微”越好?為什麼?
- Service Mesh 這一架構是為了解決微服務架構的什麼問題?
- 有沒有可能有這樣一種架構,從開發上線運維體驗上是微服務,但實際執行又類似單體服務?