限流熔斷分享
為什麼要有熔斷限流:為了防止服務雪崩。那麼什麼是服務雪崩?
下面分别講幾個概念:
快速失敗:在java中集合中 多個線程操作非安全集合 會發生快速失敗。類比到我們在開發web應用時候,對于非法參數我們也會進行提前結束(抛出異常或者直接return),避免占用之後的資源,進行無效計算。
緩存雪崩:資料庫往往是web 應用的瓶頸,為了避免頻繁讀取資料庫引入本地緩存或者分布式緩存将資料庫資料儲存在記憶體中,對于不存在緩存中資料或者未過期資料,才去資料庫讀取,減少對資料庫的通路。緩存往往會設定一定有效時間。如果在某段時間,緩存集體失效。大量資料通路湧入資料庫,稱為緩存雪崩。
分布式服務:早期使用水準拓展,對于部分伺服器可能并沒有充分利用資源,如io密集型和cpu密集型。為此引入分布式服務,特定服務使用适合伺服器,結果使用rpc或者http進行網絡傳輸。
服務雪崩:多個服務之間互相調用鍊路,一條核心鍊路往往可能調用十個服務。我們知道随着并發數增加,系統響應時間在某個時間段會急劇增加。

如果在鍊路中,某個服務發生這種情況,rt(響應時間)急劇上升,上遊服務不斷請求,造成惡性循環,上遊等待結果線程數越多,使得更上遊服務阻塞最終整條鍊路無法使用,稱為服務雪崩。
解決思路:
借鑒 快速失敗思想。發生rt逾時情況,進行快速失敗,傳回錯誤結果。——熔斷
借鑒 緩存雪崩是想。避免大量資料通路到該應用,控制數量在合理範圍。——限流
單機限流和分布式限流:
單機限流是指限定目前程序裡面的某個代碼片段的 QPS 或者 并發線程數 或者 整個機器負載指數,一旦超出規則配置的數值就會抛出異常或者傳回 false。
分布式則需要另啟一個集中的發票伺服器,這個伺服器針對每個指定的資源每秒隻會生成一定量的票數,在執行臨界區的代碼之前先去集中的發票服務領票,如果領成功了就可以執行,否則就會抛出限流異常或者傳回false。
限流算法:
業界主流的限流算法,一般有以下幾種。
1.令牌桶限流
令牌桶是一個存放固定容量令牌的桶,按照固定速率往桶裡添加令牌,填滿了就丢棄令牌,請求是否被處理要看桶中令牌是否足夠,當令牌數減為零時則拒絕新的請求。令牌桶允許一定程度突發流量,隻要有令牌就可以處理,支援一次拿多個令牌。令牌桶中裝的是令牌。
2.漏桶限流
漏桶一個固定容量的漏桶,按照固定常量速率流出請求,流入請求速率任意,當流入的請求數累積到漏桶容量時,則新流入的請求被拒絕。漏桶可以看做是一個具有固定容量、固定流出速率的隊列,漏桶限制的是請求的流出速率。漏桶中裝的是請求。
3.計數器限流
有時我們還會使用計數器來進行限流,主要用來限制一定時間内的總并發數,比如資料庫連接配接池、線程池、秒殺的并發數;計數器限流隻要一定時間内的總請求數超過設定的閥值則進行限流,是一種簡單粗暴的總數量限流,而不是平均速率限流。
4.固定視窗算法(滑動視窗算法)
在實作它的時候,首先要啟動一個定時器定期重置計數,比如你需要限制每秒鐘通路次數,而限流的邏輯就非常簡單了,隻需要比較計數值是否大于門檻值就可以了。缺點是在兩個時間間隔中間會同時湧入流量,造成視窗失敗。
主流工業方案
對于單機限流 采用Guava 的RateLimiter,用的是令牌桶的算法,主要是因為它支援突發流量,并且是比較成熟的現成架構。原因是:接口級的限流,更重要的還是要保證速率均衡,還要允許一定的突發流量。
對于分布式限流采用目前有hytrix 和Sentinel 。
Hystrix限流功能
- Hystrix使用指令模式HystrixCommand(Command)包裝依賴調用邏輯,每個指令在單獨線程中/信号授權下執行。
- 基于逾時時間降級執行fallback。
- 為每個依賴提供一個小的線程池(或信号),如果線程池已滿調用将被立即拒絕,預設不采用排隊.加速失敗判定時間。
- 依賴調用結果分:成功,失敗(抛出異常),逾時,線程拒絕,短路。 請求失敗(異常,拒絕,逾時,短路)時執行fallback(降級)邏輯。
- 熔斷器:提供基于失敗比例的熔斷(50%),停止目前依賴一段時間(10秒)。
- 提供近實時依賴的統計和監控
接口使用
詳細接口使用:
http://blog.51cto.com/developerycj/1950881使用指令模式封裝依賴邏輯
public class HelloWorldCommand extends HystrixCommand<String> {
private final String name;
public HelloWorldCommand(String name) {
//最少配置:指定指令組名(CommandGroup)
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.name = name;
}
@Override
protected String run() {
// 依賴邏輯封裝在run()方法中
return "Hello " + name +" thread:" + Thread.currentThread().getName();
}
//調用執行個體
public static void main(String[] args) throws Exception{
//每個Command對象隻能調用一次,不可以重複調用,
//重複調用對應異常資訊:This instance can only be executed once. Please instantiate a new instance.
HelloWorldCommand helloWorldCommand = new HelloWorldCommand("Synchronous-hystrix");
//使用execute()同步調用代碼,效果等同于:helloWorldCommand.queue().get();
String result = helloWorldCommand.execute();
System.out.println("result=" + result);
helloWorldCommand = new HelloWorldCommand("Asynchronous-hystrix");
//異步調用,可自由控制擷取結果時機,
Future<String> future = helloWorldCommand.queue();
//get操作不能超過command定義的逾時時間,預設:1秒
result = future.get(100, TimeUnit.MILLISECONDS);
System.out.println("result=" + result);
System.out.println("mainThread=" + Thread.currentThread().getName());
}
}
sential支援的功能
- 根據QPS限流
- 根據線程數限流
- 根據調用方限流
- 黑白名單
- 根據調用鍊限流,比如分别統計從A、B掉C接口的邏輯
- 根據資源讀寫的競争條件限流
- 異步限流接口支援
- 全局限流與單機限流
思考與總結
Sentinel本地限流通過讀取注解、代碼生成一個本地全局的context,通過context儲存本地的調用資訊:調用鍊、qps等等。當然對于注解會代理生成限流邏輯。
利用Sentinel全局限流,我們可以做基于優先級的限流,比如:當支付量大時,對其它相關的所有服務限流。
為什麼用sentinel
- hystrix隻支援api次元的熔斷、降級,功能較少
- api使用更加簡單,極低的代碼侵入性
- 豐富的限流邏輯,滿足各種需求
- 豐富的監控接口和dashboard
- 性能高以及限流的pipeline設計思路,基于前人的經驗設計更加輕量、以擴充
- 穩定且有阿裡中間件團隊背書
- 資源限流不限于接口,支援任意的調用資源
功能 | Sentinel | Hystrix(Tesla) |
---|---|---|
隔離政策 | 信号量隔離 | 線程池隔離/信号量隔離 |
熔斷降級政策 | 基于響應時間或失敗比率 | 基于失敗比率 |
實時名額實作 | 滑動視窗 | 滑動視窗(基于 RxJava) |
規則配置 | 支援多種資料源 | |
擴充性 | 多個擴充點 | 插件的形式 |
基于注解的支援 | 支援 | |
限流 | 基于 QPS,支援基于調用關系的限流 | 有限的支援 |
流量整形 | 支援慢啟動、勻速器模式 | 不支援 |
系統負載保護 | ||
控制台 | 開箱即用,可配置規則、檢視秒級監控、機器發現等 | 不完善 |
常見架構的适配 | Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet、Spring Cloud Netflix |
參考内容
Guava官方文檔-RateLimiter類Hystrix官方文檔
https://segmentfault.com/a/1190000012439580hystrix,限流入門指南
https://www.cnblogs.com/yepei/p/7169127.htmlsentinel對比hystrix
https://github.com/alibaba/Sentinel/wiki/Sentinel-%E4%B8%8E-Hystrix-%E7%9A%84%E5%AF%B9%E6%AF%94