Sentinel-Go 原始碼系列(一)|開篇

捉蟲大師發表於2021-10-19

大家好呀,打算寫一個 Go 語言元件原始碼分析系列,一是為了能學習下 Go 語言,看下別人是怎麼寫 Go 的,二是也掌握一個元件。

本次選擇了 Sentinel-Go,一是對 Java 版本的 Sentinel 算是有一些瞭解,也在生產上落地過,二是感覺他的程式碼應該不會太複雜(僅僅是感覺),三是在雲原生越來越熱的趨勢下,用 Go 實現的限流降級容錯應該是比較通用的。

原始碼閱讀本身是枯燥的,我儘量用容易理解的語言來描述,希望大家也多支援我的文章,點個在看關注就是對我最大的支援。


背景

Sentinel 簡介

隨著微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 是面向分散式服務架構的流量控制元件,主要以流量為切入點,從流量控制、熔斷降級、系統自適應保護等多個維度來幫助您保障微服務的穩定性。

Sentinel 是阿里2018年開源的專案,最初是 Java 版本,截止目前有 17.6k 的star,專案地址為

https://github.com/alibaba/Sentinel/

2020年又開源了 Go 的版本,目的是朝雲原生方向演進,截止目前 1.7k star,專案地址為

https://github.com/alibaba/sentinel-golang

Sentinel 的作用

在上面簡介中也說了,Sentinel 是微服務時代保障穩定的神兵利器

舉個例子:電商系統中使用者瀏覽商品詳情頁,通常會通過 RPC 呼叫多個微服務,查詢商品資訊的同時還會查詢使用者的資訊,也會展示優惠資訊,通常下拉選單還會展示推薦,廣告等資訊,如下圖

image

如果流量較大時,CouponService 容量不足,或者某種原因導致 RecomService 不可用,此時 AggrService 會被拖死,導致商品詳情服務不可用,但仔細想想這些服務不是那麼重要,可以進行限流或者直接降級(不再呼叫),總比直接服務不用要好吧

又或者流量實在太高,ProductService 也頂不住了,那是否可以採取限流措施,保住部分使用者的請求時正常的,也比全部不可用要好

這些問題,Sentinel 都能解決

Sentinel 提供的能力

Sentinel 將要保護的物件(可以是某個服務或一段程式碼)抽象為資源,通過動態下發的規則,對資源進行

  • 流量控制
  • 熔斷降級

針對這兩個主要功能又有很多的玩法,比如限流是針對QPS還是併發數,控制的效果是直接拒絕還是排隊等等。

當然 Sentinel 也提供一個開箱即用的 Dashboard,可擴充套件配中心進行下發規則,展示監控指標,呼叫關係鏈等等

快速開始

原始碼閱讀環境準備

  • fork 原始碼到自己倉庫,便於增加註釋
  • 拉取原始碼到本地

git clone git@github.com:lkxiaolou/sentinel-golang.git

  • 匯入 IDE,由於我既要寫 Java 又要寫 Go,所以用 IntelliJ IDEA 比較方便,只要裝一個 Go plugin 就可以了
  • 匯入後,一般 IDE 會自動下載依賴,如果沒有自動下載,試試執行( Go 安裝就不說了)

go mod download

目錄結構介紹

  • sentinel-golang
    • api:對外暴露的介面
    • core:核心實現
    • example:使用例子
    • exporter:Prometheus的exporter
    • ext:擴充套件介面,主要是動態規則配置中心擴充套件介面
    • logging:日誌模組
    • pkg:第三方外掛的實現,比如各個元件適用 Sentinel 的 adapter,以及 Sentinel 對接各種第三方配置中心的擴充套件實現
    • tests:測試類程式碼,包括單元測試、benchmark
    • util:工具類

樣例跑通

在 /example 目錄下新建 mytests 目錄,並建立一個 quick_start.go 檔案,按照官網給出的例子,先用最簡單的預設方式初始化

if err := sentinel.InitDefault(); err != nil {
	// 初始化失敗
	panic(err.Error())
}

再用寫死的方式載入規則

// 資源名
resource := "test-resource"

// 載入流控規則,寫死
_, err := flow.LoadRules([]*flow.Rule{
	{
		Resource: resource,
		// Threshold + StatIntervalInMs 可組合出多長時間限制通過多少請求,這裡相當於限制為 10 qps
		Threshold: 10,
		StatIntervalInMs: 1000,
		// 暫時不用關注這些引數
		TokenCalculateStrategy: flow.Direct,
		ControlBehavior: flow.Reject,
	},
})

最後寫測試程式碼

// 修改這個看看效果吧
currency := 100

for i := 0; i < currency; i++ {
	go func() {
		e, b := sentinel.Entry(resource, sentinel.WithTrafficType(base.Inbound))
		if b != nil {
			// 被流控
			fmt.Printf("blocked %s \n", b.BlockMsg())
		} else {
			// 通過
			fmt.Println("pass...")
			// 通過後必須呼叫Exit
			e.Exit()
		}
	}()
}

這裡限制了 10 qps,我們用 100 個協程併發測試跑一下,剛好通過10個請求

image

測試程式碼已上傳到我的倉庫

https://github.com/lkxiaolou/sentinel-golang/tree/master/example/mytests

總結

本文介紹了 Sentinel 的和它能解決的問題,以及原始碼閱讀的一些準備工作,並跑通了一個最簡單的例子,見識到了 Sentinel 限流的效果,本文先到這裡,我們下一節見。


搜尋關注微信公眾號"捉蟲大師",後端技術分享,架構設計、效能優化、原始碼閱讀、問題排查、踩坑實踐。

相關文章