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

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));
        }
    }


總結

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



財經分類資訊推薦

偷看“成人網站”,以為別人不知道?若手機出現這4種徵兆,要注意了 - 天天要聞

偷看“成人網站”,以為別人不知道?若手機出現這4種徵兆,要注意了

在當今數字化時代,互聯網已成為我們生活中不可或缺的一部分,它為我們提供了無盡的知識、娛樂與便利。然而,隨着網絡的普及,一些不良信息也隨之泛濫,其中“成人網站”便是頗具爭議的存在。許多人在好奇心或衝動之下,可能會偷偷訪問這類網站,自以為無人知曉。但事實上,任何網
“AI孫悟空”對話全球!訊飛星火AI大模型展項亮相大阪世博會中國館 - 天天要聞

“AI孫悟空”對話全球!訊飛星火AI大模型展項亮相大阪世博會中國館

4月13日,主題為“構想煥發生機的未來社會”的日本大阪·關西世博會(以下簡稱“大阪世博會”)開幕。大阪世博會中國館以“共同構建人與自然生命共同體——綠色發展的未來社會”為主題,攜“嫦娥五號”月壤樣本、“蛟龍”號體驗艙等頂尖科技成果亮相,並通過大量互動裝置和數字
如何通過戰略管理提升企業的核心競爭力和市場份額? - 天天要聞

如何通過戰略管理提升企業的核心競爭力和市場份額?

在當今風雲變幻的商業世界中,企業猶如逆水行舟,不進則退。戰略管理作為企業發展的核心驅動力,猶如指南針般為企業指明前行的方向,其重要性不言而喻。從本質上講,戰略管理是企業為實現長期目標,通過分析外部環境、評估內部資源和能力,制定、實施並評估跨職能決策的藝術和科學
讓性能隨行,創意無界!磐鐳鐳凌Link X-2顯卡塢上市 - 天天要聞

讓性能隨行,創意無界!磐鐳鐳凌Link X-2顯卡塢上市

隨着Thunderbolt接口和OCuLink接口的普及,市場上的顯卡擴展塢也逐漸豐富起來,不過基本很少會有廠商選擇年年更新,但熟悉磐鐳的老粉應該都知道,磐鐳的顯卡擴展塢業務已經做很多年了,除了開放式的鐳凌Link S系列,還有機箱式的鐳凌Link X系列,現
除菌率99.9999%!廚房好搭子容聲冰箱將上新,主打“橫掃千菌” - 天天要聞

除菌率99.9999%!廚房好搭子容聲冰箱將上新,主打“橫掃千菌”

我們品嘗家中存儲的美味時,隔夜海鮮中將滋生沙門氏菌、未密封乳製品中將滋生李斯特菌等等……這些看不見的細菌病毒,正在將本該存儲幸福的冰箱,變成潛藏在日常生活中的健康隱患。別慌,你的廚房好搭子容聲冰箱又將上新了!4月13日,容聲將攜506雙凈Max冰箱主打“凈味除
售後維修服務為何成為家電業最尷尬的環節? - 天天要聞

售後維修服務為何成為家電業最尷尬的環節?

家電售後服務的問題一直存在,不管是不是3.15都應該被受到重視,特別是時代的變遷,科學技術的進步,家電行業的演變,使得現在的家電售後服務與以往有了顯著不同,隨時跟蹤研究才具有現實意義。
GMV破千億元、用戶超1億,商家盯上了淘寶這一新藍海 - 天天要聞

GMV破千億元、用戶超1億,商家盯上了淘寶這一新藍海

在今年38大促期間,一大批消費者在淘寶搶購海底撈的代金券,為海底撈提前“囤”了一波消費者。不止美食、麗人服務類代金券,更有以京東洗衣為代表的洗護服務,以遊戲為代表的虛擬消費,以優酷、愛奇藝為代表的會員充值服務等。