逅弈 轉載請註明原創出處,謝謝!
系列文章
我們已經知道了 Sentinel 的三大功能:限流
降級
系統保護
。現在讓我們來了解下具體的使用方法,以限流來演示具體的步驟。
引入依賴
首先肯定是要先引入需要的依賴,如下所示:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>x.y.z</version>
</dependency>
複製程式碼
這裡的版本號 x.y.z 可以根據需要自行選擇,我選擇的是截至目前為止的最新版:1.4.0。
定義資源
假設我們有一個 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 的規則,那麼當且僅當來自 caller1 對 NodeA
的請求才會觸發流量控制。
- other
表示除 {some_origin_name} 以外的其餘呼叫方的流量進行流量控制。
例如:資源 NodeA
配置了一條針對呼叫者 caller1 的限流規則,同時又配置了一條呼叫者為 other 的規則,那麼任意來自非 caller1 對 NodeA
的呼叫,都不能超過 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 可以用下面這張圖表示:
規則定義好了之後,啟動應用後,就會自動對我們的業務程式碼進行保護了,當然實際生產環境中不可能通過硬編碼的方式來定義規則的,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-record 之外,還有 sentinel-exception 檔案,從命名上就可以知道這個檔案是記錄 sentinel 執行過程中出現的異常的。
讓我們開啟 sentinel-record.log.2019-01-02.0 的檔案看下具體的內容:
sentinel-record 日誌中會記錄載入好的規則等資訊,具體的實時統計日誌會在另一個 叫 xx-metrics.log.${date} 的檔案中。
檢視統計效果
首先我們要訪問一下我們的服務,觸發了 sentinel 的限流規則後,才會生成具體的統計檔案。
可以發現該方法成功返回了,現在讓我們來看看 ~/logs/csp/ 目錄下生成的統計檔案,如下圖所示:
該統計檔案的命名方式是 ${appName}-metrics.log.${date} ,其中 ${appName} 會優先獲取的系統引數 project.name 的值,如果獲取不到會從啟動引數中獲取,具體的獲取方式在 AppNameUtil 類中。
我們開啟 lememo-retircs 檔案,看到如下的資訊:
可以看到我們請求了很多次該資源後,sentinel 把每秒的統計資訊都列印出來了,用 | 來分隔不同的引數,一共有8個引數,從左至右分別是:
可以看到我們的請求都已經成功通過了,現在我們把規則中設定的 count 閾值改為1,然後重啟服務後,再次請求該服務,然後再次開啟 lememo-metrics 檔案,如下圖所示:
可以看到每秒中只有1個請求通過了,其他的都被 block 了,再看我們在程式碼中列印的日誌:
本篇文章通過一個例子對 sentinel 的限流進行了實戰,瞭解了規則的詳細作用,也知道通過 sentinel 列印的日誌來檢視執行過程中狀態。
但是這種方式比較原始,不管是建立規則,還是檢視日誌,下篇文章我將通過 sentinel 自帶的控制檯帶大家瞭解具體的作用。
更多原創好文,請關注公眾號「逅弈逐碼」