Sentinel-Go 源碼之一開篇之作
本文轉載自微信公眾號「捉蟲大師」,作者捉蟲大師。轉載本文請聯系捉蟲大師公眾號。
大家好呀,打算寫一個 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 調用多個微服務,查詢商品信息的同時還會查詢用戶的信息,也會展示優惠信息,通常下拉列表還會展示推薦,廣告等信息,如下圖
如果流量較大時,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個請求
測試代碼已上傳到我的倉庫
https://github.com/lkxiaolou/sentinel-golang/tree/master/example/mytests
總結
本文介紹了 Sentinel 的和它能解決的問題,以及源碼閱讀的一些準備工作,并跑通了一個最簡單的例子,見識到了 Sentinel 限流的效果,本文先到這里,我們下一節見。