Sentinel 實戰-限流篇

逅弈 轉載請註明原創出處,謝謝!java

系列文章

Sentinel 原理-全解析git

Sentinel 原理-調用鏈github

Sentinel 原理-滑動窗口算法

Sentinel 原理-實體類數據庫

Sentinel 實戰-控制檯篇app

Sentinel 實戰-規則持久化post

咱們已經知道了 Sentinel 的三大功能:限流 降級 系統保護。如今讓咱們來了解下具體的使用方法,以限流來演示具體的步驟。ui

引入依賴

首先確定是要先引入須要的依賴,以下所示:編碼

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>x.y.z</version>
</dependency>
複製代碼

這裏的版本號 x.y.z 能夠根據須要自行選擇,我選擇的是截至目前爲止的最新版:1.4.0。spa

定義資源

假設咱們有一個 UserService :

public class UserService {
	/** * 根據uid獲取用戶信息 * @param uid uid * @return 用戶信息 */
	public User getUser(Long uid){
	    // 業務代碼
	    User user = new User();
	    user.setUid(uid);
	    user.setName("user-" + uid);
        return user;
	}

	public static class User {
	    private Long uid;
	    private String name;
	    // 省略getter、setter
	}
}
複製代碼

如今咱們要對 getUser 方法進行限流,那首先咱們要定義一個資源,在 sentinel 中資源是抽象出來作具體的操做的,用資源來保護咱們的代碼和服務。

用戶只須要爲受保護的代碼或服務定義一個資源,而後定義規則就能夠了,剩下的都交給sentinel來處理了。定義完資源後,就能夠經過在程序中埋點來保護你本身的服務了,埋點的方式有兩種:拋出異常和返回布爾值。

下面我用拋出異常的方式進行埋點:

// 定義的資源
public static final String USER_RES = "userResource";

public User getUser(Long uid){
    Entry entry = null;
    try {
        // 流控代碼
        entry = SphU.entry(USER_RES);
        // 業務代碼
        User user = new User();
        user.setUid(uid);
        user.setName("user-" + uid);
        return user;
    }catch(BlockException e){
        // 被限流了
        System.out.println("[getUser] has been protected! Time="+System.currentTimeMillis());
    }finally {
        if(entry!=null){
            entry.exit();
        }
    }
    return null;
}
複製代碼

除了經過跑出異常的方式定義資源外,返回布爾值的方式也是同樣的,這裏不具體展開了。

PS:若是你不想對原有的業務代碼進行侵入,也能夠經過註解 SentinelResource 來進行資源埋點。

定義規則

定義完資源後,就能夠來定義限流的規則了,可是咱們須要對流控規則作個詳細的瞭解,以便更好的進行限流的操做,流控的規則對應的是 FlowRule。

一條FlowRule有如下幾個重要的屬性組成:

  • resource: 規則的資源名
  • grade: 限流閾值類型,qps 或線程數
  • count: 限流的閾值
  • limitApp: 被限制的應用,受權時候爲逗號分隔的應用集合,限流時爲單個應用
  • strategy: 基於調用關係的流量控制
  • controlBehavior:流控策略

前三個屬性比較好理解,最後三個比較難理解,讓咱們來詳細看下最後三個屬性:

limitApp

首先讓咱們來看下limitApp,從字面上看是指要限制哪一個應用的意思,主要是用於根據調用方進行流量控制。

他有三種狀況能夠選擇:

  • default

表示不區分調用者,來自任何調用者的請求都將進行限流統計。

  • {some_origin_name}

表示針對特定的調用者,只有來自這個調用者的請求才會進行流量控制。

例如:資源 NodeA 配置了一條針對調用者 caller1 的規則,那麼當且僅當來自 caller1NodeA 的請求才會觸發流量控制。

  • other

表示除 {some_origin_name} 之外的其他調用方的流量進行流量控制。

例如:資源 NodeA 配置了一條針對調用者 caller1 的限流規則,同時又配置了一條調用者爲 other 的規則,那麼任意來自非 caller1NodeA 的調用,都不能超過 other 這條規則定義的閾值。

strategy

基於調用關係的流量控制,也有三種狀況能夠選擇:

  • STRATEGY_DIRECT

根據調用方進行限流。ContextUtil.enter(resourceName, origin) 方法中的 origin 參數標明瞭調用方的身份。

若是 strategy 選擇了DIRECT ,則還須要根據限流規則中的 limitApp 字段根據調用方在不一樣的場景中進行流量控制,包括有:」全部調用方「、」特定調用方origin「、」除特定調用方origin以外的調用方「。

  • STRATEGY_RELATE

根據關聯流量限流。當兩個資源之間具備資源爭搶或者依賴關係的時候,這兩個資源便具備了關聯,可以使用關聯限流來避免具備關聯關係的資源之間過分的爭搶。

好比對數據庫同一個字段的讀操做和寫操做存在爭搶,讀的速度太高會影響寫得速度,寫的速度太高會影響讀的速度。

舉例來講:read_db 和 write_db 這兩個資源分別表明數據庫讀寫,咱們能夠給 read_db 設置限流規則來達到寫優先的目的:設置 FlowRule.strategy 爲 RuleConstant.STRATEGY_RELATE,同時設置 FlowRule.refResource 爲 write_db。這樣當寫庫操做過於頻繁時,讀數據的請求會被限流。

  • STRATEGY_CHAIN

根據調用鏈路入口限流。假設來自入口 Entrance1 和 Entrance2 的請求都調用到了資源 NodeA,Sentinel 容許根據某個入口的統計信息對資源進行限流。

舉例來講:咱們能夠設置 FlowRule.strategy 爲 RuleConstant.CHAIN,同時設置 FlowRule.refResource 爲 Entrance1 來表示只有從入口 Entrance1 的調用纔會記錄到 NodeA 的限流統計當中,而對來自 Entrance2 的調用能夠放行。

controlBehavior

流控策略,主要是發生攔截後具體的流量整形和控制策略,目前有三種策略,分別是:

  • CONTROL_BEHAVIOR_DEFAULT

這種方式是:直接拒絕,該方式是默認的流量控制方式,當 qps 超過任意規則的閾值後,新的請求就會被當即拒絕,拒絕方式爲拋出FlowException。

這種方式適用於對系統處理能力確切已知的狀況下,好比經過壓測肯定了系統的準確水位。

  • CONTROL_BEHAVIOR_WARM_UP

這種方式是:排隊等待 ,又稱爲 冷啓動。該方式主要用於當系統長期處於低水位的狀況下,流量忽然增長時,直接把系統拉昇到高水位可能瞬間把系統壓垮。

經過"冷啓動",讓經過的流量緩慢增長,在必定時間內逐漸增長到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮的狀況。

  • CONTROL_BEHAVIOR_RATE_LIMITER

這種方式是:慢啓動,又稱爲 勻速器模式。這種方式嚴格控制了請求經過的間隔時間,也便是讓請求以均勻的速度經過,對應的是漏桶算法。

這種方式主要用於處理間隔性突發的流量,例如消息隊列。想象一下這樣的場景,在某一秒有大量的請求到來,而接下來的幾秒則處於空閒狀態,咱們但願系統可以在接下來的空閒期間逐漸處理這些請求,而不是在第一秒直接拒絕多餘的請求。

具體的 FlowRule 能夠用下面這張圖表示:

flow-rule-factors.png

規則定義好了以後,啓動應用後,就會自動對咱們的業務代碼進行保護了,固然實際生產環境中不可能經過硬編碼的方式來定義規則的,sentinel 爲咱們提供了 DataSource 接口,經過實現該接口能夠自定義規則的存儲數據源。

經過 DataSource 接口能夠有不少種方式對規則進行持久化,例如:

  • 整合動態配置系統,如 ZooKeeper、Nacos 等,動態地實時刷新配置規則
  • 結合 RDBMS、NoSQL、VCS 等來實現該規則
  • 配合 Sentinel Dashboard 使用

本篇文章不對規則的持久化作具體的介紹,本篇文章主要是實現一個簡單的限流的例子的接入。

PS:DateSource 接口在後期已經被拆成 ReadableDataSource 和 WritableDataSource 接口了。

查看日誌

咱們經過一個Spring Boot項目來啓動,其中 UserService 做爲一個項目中一個具體的服務,項目啓動好以後,會在 ${userhome}/logs/csp/ 目錄下建立一個 sentinel-record.log.${date} 的日誌文件,該文件會記錄 sentinel 的重要的行爲,我本地的日誌文件以下所示:

sentinel-logs-1.png

從上圖中能夠看到,除了 sentinel-record 以外,還有 sentinel-exception 文件,從命名上就能夠知道這個文件是記錄 sentinel 運行過程當中出現的異常的。

讓咱們打開 sentinel-record.log.2019-01-02.0 的文件看下具體的內容:

sentinel-logs-2.png

sentinel-record 日誌中會記錄加載好的規則等信息,具體的實時統計日誌會在另外一個 叫 xx-metrics.log.${date} 的文件中。

查看統計效果

首先咱們要訪問一下咱們的服務,觸發了 sentinel 的限流規則後,纔會生成具體的統計文件。

call-resource.png

能夠發現該方法成功返回了,如今讓咱們來看看 ~/logs/csp/ 目錄下生成的統計文件,以下圖所示:

sentinel-logs-3.png

該統計文件的命名方式是 ${appName}-metrics.log.${date} ,其中 ${appName} 會優先獲取的系統參數 project.name 的值,若是獲取不到會從啓動參數中獲取,具體的獲取方式在 AppNameUtil 類中。

咱們打開 lememo-retircs 文件,看到以下的信息:

sentinel-logs-4.png

能夠看到咱們請求了不少次該資源後,sentinel 把每秒的統計信息都打印出來了,用 | 來分隔不一樣的參數,一共有8個參數,從左至右分別是:

sentinel-metrics-param.png

能夠看到咱們的請求都已經成功經過了,如今咱們把規則中設置的 count 閾值改成1,而後重啓服務後,再次請求該服務,而後再次打開 lememo-metrics 文件,以下圖所示:

sentinel-logs-5.png

能夠看到每秒中只有1個請求經過了,其餘的都被 block 了,再看咱們在代碼中打印的日誌:

sentinel-logs-6.png

本篇文章經過一個例子對 sentinel 的限流進行了實戰,瞭解了規則的詳細做用,也知道經過 sentinel 打印的日誌來查看運行過程當中狀態。

可是這種方式比較原始,不論是建立規則,仍是查看日誌,下篇文章我將經過 sentinel 自帶的控制檯帶你們瞭解具體的做用。

更多原創好文,請關注「逅弈逐碼」

更多原創好文,請關注公衆號「逅弈逐碼」

相關文章
相關標籤/搜索