研發:限流:Sentinel瞭解

在複雜的生產環境下可能部署着成千上萬的服務實例,當流量持續不斷地涌入,服務之間相互調用頻率陡增時,會產生系統負載過高、網絡延遲等一系列問題,從而導致某些服務不可用。如果不進行相應的流量控制,可能會導致級聯故障,並影響到服務的可用性,因此如何對高流量進行合理控制,成爲保障服務穩定性的關鍵。

Sentinel是面向分佈式服務架構的輕量級限流降級框架,以流量爲切入點,從流量控制、熔斷降級和系統負載保護等多個維度來幫助用戶保障服務的穩定性。

Sentinel 意爲哨兵,這個命名形象的詮釋了Sentinel在分佈式系統中的工作角色和重要性。 以 Sentinel 在Dubbo 生態系統中的作用爲例,Dubbo服務框架的核心模塊包括註冊中心、服務提供方、服務消費方(服務調用方)和監控4個模塊。Sentinel通過對服務提供方和服務消費方的限流來進一步提升服務的可用性。接下來我們看看Sentinel對服務提供方和服務消費方限流的技術實現方式。

Sentinel 提供了與 Dubbo 適配的模塊 – Sentinel Dubbo Adapter,包括針對服務提供方的過濾器和服務消費方的過濾器。使用時用戶只需引入相關模塊,Dubbo的服務接口和方法(包括調用端和服務端)就會成爲 Sentinel 中的資源,在配置了規則後就可以自動享受到 Sentinel 的防護能力。同時提供了靈活的配置選項,例如若不希望開啓Sentinel Dubbo Adapter中的某個Filter,可以手動關閉對應的Filter。

流量具有很強的實時性,之所以需要限流,是因爲我們無法對流量的到來作出精確的預判,不然的話我們完全可以通過彈性的計算資源來處理,所以這時候爲了保證限流的準確性,限流框架的監控功能就非常重要了。

Sentinel的控制檯(Dashboard)是流量控制、熔斷降級規則統一配置和管理的入口,同時它爲用戶提供了多個維度的監控功能。在Sentinel控制檯上,我們可以配置規則並實時查看流量控制效果。

單臺設備監控 - 當在機器列表中看到您的機器,就代表着已經成功接入控制檯,可以查看單臺設備的設備名稱、IP地址、端口號、健康狀態和心跳時間等信息。

鏈路監控 - 簇點鏈路實時的去拉取指定客戶端資源的運行情況,它提供了兩種展示模式,一種用書狀結構展示資源的調用鏈路;另外一種則不區分調用鏈路展示資源的運行情況。通過鏈路監控,可以查看到每個資源的流控和降級的歷史狀態。

聚合監控 - 同一個服務下的所有機器的簇點信息會被彙總,實現實時監控,精確度達秒級。

三、Sentinel 基於 Dubbo的最佳實踐

Dubbo 接入 Sentinel後,可通過對Dubbo核心模塊中的服務提供方和服務消費方的限流來進一步提升服務的可用性。

1、對服務提供方的限流

對服務提供方的限流可分爲服務提供方的自我保護能力和服務提供方對服務消費方的請求分配能力這兩個維度。

服務提供方用於向外界提供服務,處理各個消費方的調用請求。爲了保護提供方不被激增的流量拖垮影響穩定性,可以給提供方配置QPS模式的限流,這樣當每秒的請求量超過設定的閾值時會自動拒絕閾值外的請求。若希望整個服務接口的QPS不超過一定數值,則可以爲對應服務接口資源(resourceName爲接口全限定名)配置QPS閾值;若希望對某個服務函數的QPS不超過一定數值,則可以爲對這個服務函數資源(resourceName爲接口全限定名:方法簽名)配置QPS閾值。

根據調用方的需求來分配服務提供方的處理能力也是常見的限流方式。比如有兩個服務 A 和 B 都向 Service Provider 發起調用請求,我們希望只對來自服務 B 的請求進行限流,則可以設置限流規則的 limitApp 爲服務 B 的名稱。Sentinel Dubbo Adapter 會自動解析 Dubbo 消費方(調用方)的 application name 作爲調用方名稱(origin),在進行資源保護的時候都會帶上調用方名稱。若限流規則未配置調用方(default),則該限流規則對所有調用方生效。若限流規則配置了調用方則限流規則將僅對指定調用方生效。

2、對服務消費方的限流

對服務提供方的限流可分爲對控制併發線程數,和服務降級兩個維度。

服務消費方作爲客戶端去調用遠程服務。每一個服務都可能會依賴幾個下游服務,若某個服務A依賴的下游服務B出現了不穩定的情況,服務A請求服務B的響應時間變長,從而服務A調服務B的線程就會產生堆積,最終可能耗盡服務A的線程數。我們通過用併發線程數來控制對下游服務B的訪問,來保證下游服務不可靠的時候,不會拖垮服務自身。採用基於線程數的限制模式後,我們不需要再去對線程池進行隔離,Sentinel 會控制資源的線程數,超出的請求直接拒絕,直到堆積的線程處理完成。限流粒度同樣可以是服務接口和服務方法兩種粒度。

我們看一下這種模式的效果。假設當前服務A依賴兩個遠程服務方法 sayHello(java.lang.String) 和 doAnother()。前者遠程調用的響應時間爲1s-1.5s之間,後者RT非常小(30 ms左右)。服務A端設兩個遠程方法線程數爲5,然後每隔50 ms左右向線程池投入兩個任務,作爲調用者分別遠程調用對應方法,持續10次。可以看到 sayHello 方法被限流5次,因爲後面調用的時候前面的遠程調用還未返回(RT高);而 doAnother() 調用則不受影響。線程數目超出時快速失敗能夠有效地防止自己被調用所影響。

此外,當調用鏈路中某個資源出現不穩定的情況,如平均 RT 增高、異常比例升高的時候,Sentinel 會使對此調用資源進行降級操作。

動態規則數據源

Sentinel 的動態規則數據源用於從中讀取及寫入規則。從 0.2.0 版本開始,Sentinel 將動態規則數據源分爲兩種類型:讀數據源(ReadableDataSource)和寫數據源(WritableDataSource):

  • 讀數據源僅負責監聽或輪詢讀取遠程存儲的變更。
  • 寫數據源僅負責將規則變更寫入到規則源中。

其中讀數據源常見的實現方式有:

  • Pull 模式:客戶端主動向某個規則管理中心定期輪詢拉取規則,這個規則中心可以是 RDBMS、文件 等。這樣做的方式是簡單,缺點是可能無法及時獲取變更,拉取過於頻繁也可能會有性能問題。
  • Push 模式:規則中心統一推送,客戶端通過註冊監聽器的方式時刻監聽變化,比如使用 Nacos、Zookeeper 等配置中心。這種方式有更好的實時性和一致性保證。

在實際的場景中,不同的存儲類型對應的數據源類型也不同。對於 push 模式的數據源,一般不支持寫入;而 pull 模式的數據源則是可寫的。

下面我們分別來分析一下它們結合 Sentinel 控制檯的使用場景,以及相應的需要改造的點。

| 原始情況

若應用未註冊任何數據源,直接從 Sentinel 控制檯推送規則的過程非常簡單:

Sentinel 控制檯通過 API 將規則推送至客戶端並直接更新到內存中。這種情況下應用重啓規則就會消失,僅用於簡單測試,不能用於生產環境。一般在生產環境中,我們需要在應用端配置規則數據源。

| Pull模式的數據源

Pull 模式的數據源(如本地文件、RDBMS 等)一般是可寫入的。使用時需要在客戶端註冊數據源:將對應的讀數據源註冊至對應的 RuleManager,將寫數據源註冊至 transport 的 WritableDataSourceRegistry 中。

本地文件數據源會定時輪詢文件的變更,讀取規則。這樣我們既可以在應用本地直接修改文件來更新規則,也可以通過 Sentinel 控制檯推送規則。以本地文件數據源爲例,推送過程如下圖所示:

首先 Sentinel 控制檯通過 API 將規則推送至客戶端並更新到內存中,接着註冊的寫數據源會將新的規則保存到本地的文件中。使用 pull 模式的數據源時一般不需要對 Sentinel 控制檯進行改造。

| Push模式的數據源

對於 push 模式的數據源(如遠程配置中心),推送的操作不應由 Sentinel 數據源進行,而應該經控制檯進行推送,數據源僅負責獲取配置中心推送的配置並更新到本地。

假設寫入的操作也由數據源進行,那麼 Sentinel 客戶端收到控制檯推送的規則後,將新的規則更新到內存中,同時將規則推送至遠程的配置中心。此時,數據源監聽到配置中心推送過來的新規則,又一次更新到內存中。也就是說應用在本地更新完規則並推送到遠程後,又要接收變更並更新一次,這樣顯然是不合理的。因此推送規則正確做法應該是 配置中心控制檯/Sentinel 控制檯 → 配置中心 → Sentinel 數據源 → Sentinel,而不是經 Sentinel 數據源推送至配置中心。這樣的流程就非常清晰了:

注意由於不同的生產環境可能使用不同的數據源,從 Sentinel 控制檯推送至配置中心的實現需要用戶自行改造。以 ZooKeeper 爲例,我們可以按照如下步驟進行改造(假設推送維度爲應用維度):

1. 實現一個公共的 ZooKeeper 客戶端用於推送規則,在 Sentinel 控制檯配置項中需要指定 ZooKeeper 的地址,啓動時即創建 ZooKeeper Client。

2. 我們需要針對每個應用(appName),每種規則設置不同的 path(可隨時修改);或者約定大於配置(如 path 的模式統一爲 /sentinel_rules/{appName}/{ruleType},e.g. sentinel_rules/appA/flowRule)。

3. 規則配置頁需要進行相應的改造,直接針對應用維度進行規則配置;修改同個應用多個資源的規則時可以批量進行推送,也可以分別推送。Sentinel 控制檯將規則緩存在內存中(如 InMemFlowRuleStore),可以對其進行改造使其支持應用維度的規則緩存(key 爲 appName),每次添加/修改/刪除規則都先更新內存中的規則緩存,然後需要推送的時候從規則緩存中獲取全量規則,然後通過上面實現的 Client 將規則推送到 ZooKeeper 即可。

4. 應用客戶端需要註冊對應的讀數據源以監聽變更,可以參考相關文檔。

監控數據持久化

Sentinel 會記錄資源訪問的秒級數據(若沒有訪問則不進行記錄)並保存在本地日誌中,具體格式請見 秒級監控日誌文檔。Sentinel 控制檯通過 Sentinel 客戶端預留的 API 從秒級監控日誌中拉取監控數據,並進行聚合。目前 Sentinel 控制檯中監控數據聚合後直接存在內存中,未進行持久化,且僅保留最近 5 分鐘的監控數據。若需要監控數據持久化的功能,可以自行擴展實現 MetricsRepository 接口(0.2.0 版本),然後註冊成 Spring Bean 並在相應位置通過 @Qualifier 註解指定對應的 bean name 即可。MetricsRepository 接口定義了以下功能:

  • savesaveAll:存儲對應的監控數據
  • queryByAppAndResourceBetween:查詢某段時間內的某個應用的某個資源的監控數據
  • listResourcesOfApp:查詢某個應用下的所有資源

其中默認的監控數據類型爲 MetricEntity,包含應用名稱、時間戳、資源名稱、異常數、請求通過數、請求 block 數、平均響應時間等信息。

同時用戶可以自行進行擴展,適配 Grafana 等可視化平臺,以便將監控數據更好地進行可視化。

Sentinel與Hystrix對比

參考鏈接:

Sentinel 控制檯應用於生產環境

Sentinel 如何通過勻速請求和冷啓動來保障服務的穩定性

Sentinel如何通過限流實現服務的高可用性

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章