微服務之熔斷器

熔斷器模式可以防止應用程序不斷地嘗試執行可能會失敗的操作,使得應用程序繼續執行而不用等待修正錯誤,或者浪費CPU時間去等到長時間的超時產生。熔斷器模式也可以使應用程序能夠診斷錯誤是否已經修正,如果已經修正,應用程序會再次嘗試調用操作。

問題描述

假設我們有兩個服務serviceA、serviceB,serviceA可以正常對外提供服務,某些API依賴serviceB。

  • 當serviceB異常完全不可用時,serviceA會一直調用serviceB,返回報錯信息,健壯一點的程序都會處理這些錯誤,不至於讓服務崩潰。
  • 當serviceB響應變慢,處理請求的能力變低時,這種情況下serviceA一直等待請求完成,最終會導致資源耗盡。

因此我們需要一個機制,當下遊服務變得不可用(或者異常增多時),需要切斷對下游服務的訪問,直接返回錯誤值或者默認值

我們的熔斷器應該要滿足以下要求:

  • 自定義回退:嘗試從其他來源獲取相同的數據如果不可能,請使用自己的緩存值。
  • 快速失敗:如果serviceA知道失敗serviceB。了,那麼就沒有必要等待超時並消耗自己的資源它應該儘快返回“知道” serviceB已關閉
  • 不要崩潰:正如我們在這種情況下看到的那樣,serviceA不應該崩潰。
  • 自動修復:定期檢查是否serviceB再次起作用。
  • 其他API應該有效:所有其他API應該繼續工作。

熔斷機制

我們使用brakes包來實現熔斷機制

'use strict';

const Brakes = require('brakes');
const Promise = require('bluebird');

function promiseCall() {
  return new Promise((resolve, reject) => {
    if (Math.random() > 0.8) {
      console.log('運行成功');
      resolve('正常值');
    } else {
      console.error('運行失敗');
      reject(new Error());
    }
  });
}

function fallbackCall() { // 降級方案
  return new Promise(resolve => {
    resolve('降級方案返回錯誤值或緩存值');
  });
}

function healthCheckCall() { // 健康檢查
  return new Promise((resolve, reject) => {
    // this will return 20% true, 80% false
    if (Math.random() < 0.8) {
      console.log('\n健康檢查通過');
      resolve();
    } else {
      console.error('\n健康檢查失敗');
      reject(new Error('health check failed'));
    }
  });
}

const brake = new Brakes(promiseCall, {
  timeout: 150,
  threshold: 0.5, // 成功請求比例閾值
  waitThreshold: 10, // 等待對應請求數量之後統計成功請求比例
  healthCheckInterval: 2000, // 健康檢查間隔
});

brake.fallback(fallbackCall);
brake.healthCheck(healthCheckCall);

brake.on('circuitClosed', () => {
  console.log('\n服務恢復');
});
brake.on('circuitOpen', () => {
  console.log('\n服務熔斷');
});

const main = async () => {
  for (let i = 0; i < 2000; i++) {
    try {
      const result = await brake.exec();
      await Promise.delay(300);
      console.log(i, result);
    } catch (error) {
      console.error(i, error.message);
    }
  }
};

main().then(console.log).catch(console.error);

執行結果

// 開始時是正常運行的
運行成功
0 '正常值'
運行失敗
1 '降級方案返回錯誤值或緩存值'
運行成功
2 '正常值'
運行失敗
3 '降級方案返回錯誤值或緩存值'
運行失敗
4 '降級方案返回錯誤值或緩存值'
運行失敗
5 '降級方案返回錯誤值或緩存值'
運行失敗
6 '降級方案返回錯誤值或緩存值'
運行失敗
7 '降級方案返回錯誤值或緩存值'
運行失敗
8 '降級方案返回錯誤值或緩存值'
運行失敗
9 '降級方案返回錯誤值或緩存值'
// 服務熔斷後, 不再執行程序,直接使用降級方案
服務熔斷
10 '正常值'
11 '降級方案返回錯誤值或緩存值'
12 '降級方案返回錯誤值或緩存值'
13 '降級方案返回錯誤值或緩存值'
14 '降級方案返回錯誤值或緩存值'
15 '降級方案返回錯誤值或緩存值'
// 健康檢查失敗時,繼續使用降級方案
16 '降級方案返回錯誤值或緩存值'
17 '降級方案返回錯誤值或緩存值'
18 '降級方案返回錯誤值或緩存值'
19 '降級方案返回錯誤值或緩存值'
20 '降級方案返回錯誤值或緩存值'
21 '降級方案返回錯誤值或緩存值'
22 '降級方案返回錯誤值或緩存值'
// 健康檢查通過時,服務恢復

健康檢查通過

服務恢復
23 '降級方案返回錯誤值或緩存值'
運行失敗
24 '降級方案返回錯誤值或緩存值'
運行失敗
25 '降級方案返回錯誤值或緩存值'
運行成功
26 '正常值'
運行失敗
27 '降級方案返回錯誤值或緩存值'

不用人工干預,自動實現服務異常時熔斷、服務修復後自動恢復請求

建議在使用的時候直接封裝在客戶端對外調用邏輯裏面,上層調用不需要知道有斷路器的存在

One more thing

下游服務的報警需要設置好

Ref

簡書-Nodejs 熔斷機制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章