你能區分熔斷和降級有什麼區別嗎?

2023年04月28日20:47:10 財經 1906

熔斷和降級都是系統自我保護的一種機制,但二者又有所不同,它們的區別主要體現在以下幾點:

  1. 概念不同
  2. 觸發條件不同
  3. 歸屬關係不同

1.概念不同

1.1 熔斷概念

「熔斷」一詞早期來自股票市場。熔斷(Circuit Breaker)也叫自動停盤機制,是指當股指波幅達到規定的熔斷點時,交易所為控制風險採取的暫停交易措施。比如 2020 年 3 月 9 日,紐約股市開盤出現暴跌,隨後跌幅達到 7% 上限,觸發熔斷機制,停止交易 15 分中,恢復交易後跌幅有所減緩。

熔斷在程序中,表示「斷開」的意思。如發生了某事件,程序為了整體的穩定性,所以暫時(斷開)停止服務一段時間,以保證程序可用時再被使用。

如果沒有熔斷機制的話,會導致聯機故障和服務雪崩等問題,如下圖所示:

你能區分熔斷和降級有什麼區別嗎? - 天天要聞

1.2 降級概念

降級(Degradation)降低級別的意思,它是指程序在出現問題時,仍能保證有限功能可用的一種機制。

比如電商交易系統在雙 11 時,使用的人比較多,此時如果開放所有功能,可能會導致系統不可用,所以此時可以開啟降級功能,優先保證支付功能可用,而其他非核心功能,如評論、物流、商品介紹等功能可以暫時關閉。

所以,從上述信息可以看出:降級是一種退而求其次的選擇,而熔斷卻是整體不可用

2.觸發條件不同

不同框架的熔斷和降級的觸發條件是不同的,本文咱們以經典的 Spring Cloud 組件 Hystrix 為例,來說明觸發條件的問題。

2.1 Hystrix 熔斷觸發條件

默認情況 hystrix 如果檢測到 10 秒內請求的失敗率超過 50%,就觸發熔斷機制。之後每隔 5 秒重新嘗試請求微服務,如果微服務不能響應,繼續走熔斷機制。如果微服務可達,則關閉熔斷機制,恢復正常請求。

你能區分熔斷和降級有什麼區別嗎? - 天天要聞

2.2 Hystrix 降級觸發條件

默認情況下,hystrix 在以下 4 種條件下都會觸發降級機制:

  1. 方法拋出 HystrixBadRequestException
  2. 方法調用超時
  3. 熔斷器開啟攔截調用
  4. 線程池或隊列或信號量已滿

雖然 hystrix 組件的觸發機制,不能代表所有的熔斷和降級機制,但足矣說明此問題。

3.歸屬關係不同

**熔斷時可能會調用降級機制,而降級時通常不會調用熔斷機制。**因為熔斷是從全局出發,為了保證系統穩定性而停用服務,而降級是退而求其次,提供一種保底的解決方案,所以它們的歸屬關係是不同(熔斷 > 降級)。

題外話

當然,某些框架如 Sentinel,它早期在 Dashboard 控制台中可能叫「降級」,但在新版中新版本又叫「熔斷」,如下圖所示:

你能區分熔斷和降級有什麼區別嗎? - 天天要聞

但在兩個版本中都是通過同一個異常類型 DegradeException 來監聽的,如下代碼所示:

你能區分熔斷和降級有什麼區別嗎? - 天天要聞

所以,在 Sentinel 中,熔斷和降級功能指的都是同一件事,也側面證明了「熔斷」和「降級」概念的相似性。但我們要知道它們本質上是不同的,就像兩個雙胞胎,不能因為他們長得像,就說他們是同一個人。


你能區分熔斷和降級有什麼區別嗎? - 天天要聞

當用戶請求 A、P、H、I 四個服務獲取數據時,在正常流量下系統穩定運行,如果某天系統進來大量流量,其中服務 I 出現 CPU、內存佔用過高等問題,結果導致服務 I 出現延遲、響應過慢,隨著請求的持續增加,服務 I 承受不住壓力導致內部錯誤或資源耗盡,一直不響應,此時更糟糕的是其他服務對 I 有依賴,那麼這些依賴 I 的服務一直等待 I 的響應,也會出現請求堆積、資源佔用,慢慢擴散到所有微服務,引發雪崩效應。

基本的容錯模式

常見的容錯模式主要包含以下幾種方式:

1.主動超時:Http請求主動設置一個超時時間,超時就直接返回,不會造成服務堆積

2.限流:限制最大並發數

3.熔斷:當錯誤數超過閾值時快速失敗,不調用後端服務,同時隔一定時間放幾個請求去重試後端服務是否能正常調用,如果成功則關閉熔斷狀態,失敗則繼續快速失敗,直接返回。(此處有個重試,重試就是彈性恢復的能力)

4.隔離:把每個依賴或調用的服務都隔離開來,防止級聯失敗引起整體服務不可用

5.降級:服務失敗或異常後,返回指定的默認信息

你能區分熔斷和降級有什麼區別嗎? - 天天要聞

服務降級

由於爆炸性的流量衝擊,對一些服務進行有策略的放棄,以此緩解系統壓力,保證目前主要業務的正常運行。它主要是針對非正常情況下的應急服務措施:當此時一些業務服務無法執行時,給出一個統一的返回結果。

服務熔斷

熔斷這一概念來源於電子工程中的斷路器(Circuit Breaker)。在互聯網系統中,當下游服務因訪問壓力過大而響應變慢或失敗,上游服務為了保護系統整體的可用性,可以暫時切斷對下游服務的調用。

服務熔斷與服務降級比較

服務熔斷對服務提供了proxy,防止服務不可能時,出現串聯故障(cascading failure),導致雪崩效應。

服務熔斷一般是某個服務(下游服務)故障引起,而服務降級一般是從整體負荷考慮

1.共性:

目的 -> 都是從可用性、可靠性出發,提高系統的容錯能力。

最終表現->使某一些應用不可達或不可用,來保證整體系統穩定。

粒度 -> 一般都是服務級別,但也有細粒度的層面:如做到數據持久層、只許查詢不許增刪改等。

自治 -> 對其自治性要求很高。都要求具有較高的自動處理機制。

2.區別:

觸發原因 -> 服務熔斷通常是下級服務故障引起;服務降級通常為整體系統而考慮。

管理目標 -> 熔斷是每個微服務都需要的,是一個框架級的處理;而服務降級一般是關注業務,對業務進行考慮,抓住業務的層級,從而決定在哪一層上進行處理:比如在IO層,業務邏輯層,還是在外圍進行處理。

實現方式 -> 代碼實現中的差異。

服務熔斷恢復需注意的問題

如果服務是冪等性的,則恢復重試不會有問題;而如果服務是非冪等性的,則重試會導致數據出現問題。

Java 實現限流

1 滑動窗口

public class WindowLimiterComponent implements LimiterComponent {

    /**
     * 隊列id和隊列的映射關係,隊列裡面存儲的是每一次通過時候的時間戳,這樣可以使得程序里有多個限流隊列
     */
    private final Map<String, List<Long>> MAP = new ConcurrentHashMap<>();

    /**
     * 限制次數
     */
    private final int count;

    /**
     * 時間窗口大小
     */
    private final long timePeriod;

    public WindowLimiterComponent(int count, long timePeriod) {
        this.count = count;
        this.timePeriod = timePeriod;
    }

    /**
     * 滑動時間窗口限流演算法
     * 在指定時間窗口,指定限制次數內,是否允許通過
     *
     * @param id 隊列id
     * @return 是否被限流
     */
    @Override
    public synchronized boolean isLimited(String id) {
        // 獲取當前時間
        long nowTime = System.currentTimeMillis();
        // 根據隊列id,取出對應的限流隊列,若沒有則創建
        List<Long> list = MAP.computeIfAbsent(id, k -> new LinkedList<>());
        // 如果隊列還沒滿,則允許通過,並添加當前時間戳到隊列開始位置
        if (list.size() < count) {
            list.add(0, nowTime);
            return false;
        }
        // 隊列已滿(達到限制次數),則獲取隊列中最早添加的時間戳
        Long lastTime = list.get(count - 1);
        // 用當前時間戳 減去 最早添加的時間戳
        if (nowTime - lastTime <= timePeriod) {
            // 若結果小於等於timePeriod,則說明在timePeriod內,通過的次數大於count
            // 不允許通過
            return true;
        } else {
            // 若結果大於timePeriod,則說明在timePeriod內,通過的次數小於等於count
            // 允許通過,並刪除最早添加的時間戳,將當前時間添加到隊列開始位置
            list.remove(count - 1);
            list.add(0, nowTime);
            return false;
        }
    }

}
複製代碼
    @Test
    public void test() throws InterruptedException {
        // 任意10秒內,只允許2次通過
        LimiterComponent component = new WindowLimiterComponent(2, 10000L);
        while (true) {

            System.out.println(LocalTime.now().toString() + component.isLimited("1"));
            // 睡眠0-10秒
            Thread.sleep(1000 * new Random().nextInt(10));
        }
    }
複製代碼

2 Redis zset

public class RedisZSetLimiterComponent implements LimiterComponent{

    private final redissonComponent redissonComponent;

    /**
     * 限制次數
     */
    private final int count;

    /**
     * 時間窗口大小,單位毫秒
     */
    private final long timePeriod;


    public RedisZSetLimiterComponent(RedissonComponent component) {
        this.redissonComponent = component;
        this.count = 5;
        this.timePeriod = 1000;
    }

    public RedisZSetLimiterComponent(RedissonComponent component, int count, long timePeriod) {
        this.redissonComponent = component;
        this.count = count;
        this.timePeriod = timePeriod;
    }

    /**
     * 基於 zset 的滑動時間窗口限流演算法
     * 在指定時間窗口,指定限制次數內,是否允許通過
     *
     * @param key 隊列key
     * @return 是否允許通過
     */
    @Override
    public synchronized boolean isLimited(String key) {
        // 獲取當前時間
        long nowTime = System.currentTimeMillis();
        RScoredSortedSet<String> set = redissonComponent.getRScoredSortedSet(key);
        // 移除一個時間段以前的
        set.removeRangeByScore(0, true, (double) (nowTime - timePeriod), true);
        // 獲取集合內元素總數
        int size = set.count((double) (nowTime - timePeriod), true, nowTime, true);
        // 如果隊列沒滿
        if (size < count) {
            // 當前時間加入集合
            set.add((double) nowTime, String.valueOf(nowTime));
            return false;
        }
        return true;
    }


}
複製代碼
    @Test
    public void test() throws InterruptedException {
        // 任意10秒內,只允許2次通過
        LimiterComponent component = new RedisZSetLimiterComponent(redissonComponent, 2, 10000L);
        while (true) {

            System.out.println(LocalTime.now().toString() + component.isLimited("1"));
            // 睡眠0-10秒
            Thread.sleep(1000 * new Random().nextInt(10));
        }
    }
複製代碼

3 guava RateLimiter

@SuppressWarnings("UnstableApiUsage")
public class GuavaLimiterComponent implements LimiterComponent {

    private final int count;

    private final long timePeriod;

    private final Map<String, RateLimiter> MAP = new ConcurrentHashMap<>();

    public GuavaLimiterComponent(int count, long timePeriod) {
        this.count = count;
        this.timePeriod = timePeriod;
    }

    /**
     * 令牌桶演算法
     *
     * @param key 鍵值
     * @return 是否被限流
     */
    @Override
    public synchronized boolean isLimited(String key) {
        RateLimiter rateLimiter = MAP.computeIfAbsent(key, k -> RateLimiter.create(count, timePeriod, TimeUnit.MILLISECONDS));
        return !rateLimiter.tryAcquire();
    }


}
複製代碼
    @Test
    public void test() throws InterruptedException {
        // 任意10秒內,只允許2次通過
        LimiterComponent component = new GuavaLimiterComponent(2, 10000L);
        while (true) {
            System.out.println(LocalTime.now().toString() + component.isLimited("1"));
            // 睡眠0-10秒
            Thread.sleep(1000 * new Random().nextInt(10));
        }
    }
複製代碼

4 redisson RRateLimiter

public class RedisRateLimiterComponent implements LimiterComponent {

    private final RedissonComponent redissonComponent;

    /**
     * 限制次數
     */
    private final int count;

    /**
     * 時間窗口大小,單位毫秒
     */
    private final long timePeriod;


    public RedisRateLimiterComponent(RedissonComponent component) {
        this.redissonComponent = component;
        this.count = 5;
        this.timePeriod = 1000;
    }

    public RedisRateLimiterComponent(RedissonComponent component, int count, long timePeriod) {
        this.redissonComponent = component;
        this.count = count;
        this.timePeriod = timePeriod;
    }

    /**
     * 基於 rateLimiter 的滑動時間窗口限流演算法
     * 在指定時間窗口,指定限制次數內,是否允許通過
     *
     * @param key 隊列key
     * @return 是否允許通過
     */
    @Override
    public synchronized boolean isLimited(String key) {
        RRateLimiter rateLimiter = redissonComponent.getRateLimiter(key);
        rateLimiter.trySetRate(RateType.PER_CLIENT, count, timePeriod, RateIntervalUnit.MILLISECONDS);
        return !rateLimiter.tryAcquire();
    }


}
複製代碼
    @Test
    public void test() throws InterruptedException {
        // 任意10秒內,只允許2次通過
        LimiterComponent component = new RedisRateLimiterComponent(redissonComponent, 2, 10000L);
        while (true) {

            System.out.println(LocalTime.now().toString() + component.isLimited("1"));
            // 睡眠0-10秒
            Thread.sleep(1000 * new Random().nextInt(10));
        }
    }


總結

熔斷和降級都是程序在我保護的一種機制,但二者在概念、觸發條件、歸屬關係上都是不同的。熔斷更偏向於全局視角的自我保護(機制),而降級則偏向於具體模塊「退而請其次」的解決方案。



財經分類資訊推薦

大跳水!暴跌40% - 天天要聞

大跳水!暴跌40%

夏天快來了,「日啖荔枝三百顆,不辭長作嶺南人」的時節又到了。近日,記者從寧波果品批發市場了解到,今年「荔枝季」,大家大概率能敞開肚皮,盡情品嘗。新鮮荔枝「凌晨剛到的300箱『白糖罌』荔枝,一上午就賣出200多箱!」攤位吳老闆整理著剩餘的存貨,絲毫不擔心銷路,「幾十箱的量,再給我12個小時可以全部售空。」找吳...
明天A(12號)走勢預測 - 天天要聞

明天A(12號)走勢預測

一、技術面與指數區間上證指數短期面臨3350-3400點強阻力區,突破需量能持續放大(單日成交額1.2萬億元以上)。若量能不足,可能回踩3320點附近支撐位。東吳證券模型預測5月可能形成「倒V型」走勢,上半旬反彈後下半旬受關稅等壓力影響調整
確認了,漲價!很多人愛吃 - 天天要聞

確認了,漲價!很多人愛吃

近日,瑪氏、費列羅被曝準備對旗下巧克力產品進行漲價。5月8日,記者從多個零售商了解到,包括瑪氏、費列羅等多個巧克力品牌確實在近期發布了漲價通知。一家國內大型連鎖超市透露,其已經接到通知,瑪氏部分單品漲價,漲價幅度在4.9%-15%不等,暫時
前四月全國鐵路完成固定資產投資1947億元,同比增長5.3% - 天天要聞

前四月全國鐵路完成固定資產投資1947億元,同比增長5.3%

2025年4月8日,浙江省湖州市德清縣,在杭德市域鐵路項目建設現場,工人正在抓緊作業。視覺中國 資料圖5月11日,澎湃新聞記者從中國國家鐵路集團有限公司(下稱「國鐵集團」)獲悉,今年1至4月,鐵路建設優質高效推進,全國鐵路完成固定資產投資1
享道出行完成過13億元C輪融資;滬上阿姨登陸港交所,躋身今年第四家新茶飲IPO丨全球投融資周報05.03-05.09 - 天天要聞

享道出行完成過13億元C輪融資;滬上阿姨登陸港交所,躋身今年第四家新茶飲IPO丨全球投融資周報05.03-05.09

全球投融資周報睿獸分析每周整理當周最值得關注的國內外熱門投融資事件,幫助大家及時了解全球市場動向。一級市場投融資概覽根據睿獸分析監測數據,本周國內一級市場披露的融資事件52個, 與上周相比減少41個, 其中已披露融資金額的事件27個, 總融資規模為36.46億元人民幣, 平均融資金額為1.35億元人民幣。行業分布從融...
面對強勁反彈的美股,被散戶「逼空」的機構很尷尬 - 天天要聞

面對強勁反彈的美股,被散戶「逼空」的機構很尷尬

近期,美股市場在紛紛擾擾中走出了一波出人意料的強勁反彈。標普500指數自4月8日觸底以來,短短一個月內飆升14%。然而,這輪漲勢的主導者並非華爾街的專業機構,反而是無所畏懼、大舉「抄底」的散戶投資者——機構們此前因對經濟增長放緩和貿易摩擦的
齊國旗,突遭意外離世 - 天天要聞

齊國旗,突遭意外離世

5月10日,據券商中國報道,國海證券原總裁齊國旗近日因遭遇交通意外,不幸離世,享年61歲。齊國旗靈堂設於家鄉,故交舊友與昔日同事陸續前往弔唁。
中國外貿逆風翻紅!一季度進出口「U型反彈」超預期 - 天天要聞

中國外貿逆風翻紅!一季度進出口「U型反彈」超預期

「1月跌、2月平、3月猛衝!」海關總署最新數據顯示,今年一季度中國進出口總值達10.3萬億元,同比增長1.3%,月度增速畫出漂亮上揚線——從1月下降2.2%到3月勁增6%,硬核上演「U型反彈」。三大引擎驅動逆襲1️⃣ 52.9萬家外貿企業中
何為「促進型立法」?民營經濟促進法告訴你 - 天天要聞

何為「促進型立法」?民營經濟促進法告訴你

十四屆全國人大常委會第十五次會議4月30日表決通過《中華人民共和國民營經濟促進法》,自2025年5月20日施行。改革開放40多年來,我國民營經濟從小到大、從弱到強,取得了長足發展,也面臨著困難和挑戰。