逅弈 轉載請註明原創出處,謝謝!java
Sentinel 原理-調用鏈github
咱們已經知道了 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有如下幾個重要的屬性組成:
前三個屬性比較好理解,最後三個比較難理解,讓咱們來詳細看下最後三個屬性:
首先讓咱們來看下limitApp,從字面上看是指要限制哪一個應用的意思,主要是用於根據調用方進行流量控制。
他有三種狀況能夠選擇:
表示不區分調用者,來自任何調用者的請求都將進行限流統計。
表示針對特定的調用者,只有來自這個調用者的請求才會進行流量控制。
例如:資源 NodeA
配置了一條針對調用者 caller1 的規則,那麼當且僅當來自 caller1 對 NodeA
的請求才會觸發流量控制。
表示除 {some_origin_name} 之外的其他調用方的流量進行流量控制。
例如:資源 NodeA
配置了一條針對調用者 caller1 的限流規則,同時又配置了一條調用者爲 other 的規則,那麼任意來自非 caller1 對 NodeA
的調用,都不能超過 other 這條規則定義的閾值。
基於調用關係的流量控制,也有三種狀況能夠選擇:
根據調用方進行限流。ContextUtil.enter(resourceName, origin) 方法中的 origin 參數標明瞭調用方的身份。
若是 strategy 選擇了DIRECT ,則還須要根據限流規則中的 limitApp 字段根據調用方在不一樣的場景中進行流量控制,包括有:」全部調用方「、」特定調用方origin「、」除特定調用方origin以外的調用方「。
根據關聯流量限流。當兩個資源之間具備資源爭搶或者依賴關係的時候,這兩個資源便具備了關聯,可以使用關聯限流來避免具備關聯關係的資源之間過分的爭搶。
好比對數據庫同一個字段的讀操做和寫操做存在爭搶,讀的速度太高會影響寫得速度,寫的速度太高會影響讀的速度。
舉例來講:read_db 和 write_db 這兩個資源分別表明數據庫讀寫,咱們能夠給 read_db 設置限流規則來達到寫優先的目的:設置 FlowRule.strategy 爲 RuleConstant.STRATEGY_RELATE,同時設置 FlowRule.refResource 爲 write_db。這樣當寫庫操做過於頻繁時,讀數據的請求會被限流。
根據調用鏈路入口限流。假設來自入口 Entrance1 和 Entrance2 的請求都調用到了資源 NodeA,Sentinel 容許根據某個入口的統計信息對資源進行限流。
舉例來講:咱們能夠設置 FlowRule.strategy 爲 RuleConstant.CHAIN,同時設置 FlowRule.refResource 爲 Entrance1 來表示只有從入口 Entrance1 的調用纔會記錄到 NodeA 的限流統計當中,而對來自 Entrance2 的調用能夠放行。
流控策略,主要是發生攔截後具體的流量整形和控制策略,目前有三種策略,分別是:
這種方式是:直接拒絕,該方式是默認的流量控制方式,當 qps 超過任意規則的閾值後,新的請求就會被當即拒絕,拒絕方式爲拋出FlowException。
這種方式適用於對系統處理能力確切已知的狀況下,好比經過壓測肯定了系統的準確水位。
這種方式是:排隊等待 ,又稱爲 冷啓動。該方式主要用於當系統長期處於低水位的狀況下,流量忽然增長時,直接把系統拉昇到高水位可能瞬間把系統壓垮。
經過"冷啓動",讓經過的流量緩慢增長,在必定時間內逐漸增長到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮的狀況。
這種方式是:慢啓動,又稱爲 勻速器模式。這種方式嚴格控制了請求經過的間隔時間,也便是讓請求以均勻的速度經過,對應的是漏桶算法。
這種方式主要用於處理間隔性突發的流量,例如消息隊列。想象一下這樣的場景,在某一秒有大量的請求到來,而接下來的幾秒則處於空閒狀態,咱們但願系統可以在接下來的空閒期間逐漸處理這些請求,而不是在第一秒直接拒絕多餘的請求。
具體的 FlowRule 能夠用下面這張圖表示:
規則定義好了以後,啓動應用後,就會自動對咱們的業務代碼進行保護了,固然實際生產環境中不可能經過硬編碼的方式來定義規則的,sentinel 爲咱們提供了 DataSource 接口,經過實現該接口能夠自定義規則的存儲數據源。
經過 DataSource 接口能夠有不少種方式對規則進行持久化,例如:
本篇文章不對規則的持久化作具體的介紹,本篇文章主要是實現一個簡單的限流的例子的接入。
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 自帶的控制檯帶你們瞭解具體的做用。
更多原創好文,請關注公衆號「逅弈逐碼」