溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Node.js中斷路器機制是怎么樣的

發(fā)布時間:2021-10-18 10:02:55 來源:億速云 閱讀:127 作者:小新 欄目:web開發(fā)

這篇文章將為大家詳細講解有關Node.js中斷路器機制是怎么樣的,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

架構演變帶來的問題

當我們使用傳統(tǒng)的 CS 架構時,服務端由于故障等原因將請求堵塞,可能會導致客戶端的請求失去響應,進而在一段時間后導致一批用戶無法獲得服務。而這種情況可能影響范圍是有限,可以預估的。

然而,在微服務體系下,您的服務器可能依賴了若干其他微服務,而這些微服務又依賴其它更多的微服務,這種情況下,某個服務對于下游的堵塞,可能會瞬間(數(shù)秒內)因為級聯(lián)的資源消耗造成整條鏈路上災難性的后果,我們稱之為“服務血崩”?!就扑]學習:《nodejs 教程》】

Node.js中斷路器機制是怎么樣的

Node.js中斷路器機制是怎么樣的

解決問題的幾種方式

  • 熔斷模式:顧名思義,就如同家用電路一樣,如果一條線路電壓過高,保險絲會熔斷,防止火災。在使用熔斷模式的系統(tǒng)中,如果發(fā)現(xiàn)上游服務調用慢,或者有大量超時的時候,直接中止對于該服務的調用,直接返回信息,快速釋放資源。直至上游服務好轉時再恢復調用。

  • 隔離模式:將不同的資源或者服務的調用分割成幾個不同的請求池,一個池子的資源被耗盡并不會影響其它資源的請求,防止某個單點的故障消耗完全部的資源。這是非常傳統(tǒng)的一種容災設計。

  • 限流模式:熔斷和隔離都是一種事后處置的方式,限流模式則可以在問題出現(xiàn)之前降低問題出現(xiàn)的概率。限流模式可以對某些服務的請求設置一個最高的 QPS 閾值,超出閾值的請求直接返回,不再占用資源處理。但是限流模式,并不能解決服務血崩的問題,因為往往引起血崩并不是因為請求的數(shù)量大,而是因為多個級聯(lián)層數(shù)的放大。

斷路器的機制和實現(xiàn)

斷路器的存在,相當于給了我們一層保障,在調用穩(wěn)定性欠佳,或者說很可能會調用失敗的服務和資源時,斷路器可以監(jiān)視這些錯誤并且在達到一定閾值之后讓請求失敗,防止過度消耗資源。并且,斷路器還擁有自動識別服務狀態(tài)并恢復的功能,當上游服務恢復正常時,斷路器可以自動判斷并恢復正常請求。

讓我們看一下一個沒有斷路器的請求過程: 用戶依賴 ServiceA 來提供服務,ServiceA 又依賴 ServiceB 提供的服務,假設 ServiceB 此時出現(xiàn)了故障,在 10 分鐘內,對于每個請求都會延遲 10 秒響應。

Node.js中斷路器機制是怎么樣的

那么假設我們有 N 個 User 在請求 ServiceA 的服務時,幾秒鐘內,ServiceA 的資源就會因為對 ServiceB 發(fā)起的請求被掛起而消耗一空,從而拒絕 User 之后的任何請求。對于用戶來說,這就等于 ServiceA 和 ServiceB 同時都出現(xiàn)了故障,引起了整條服務鏈路的崩潰。

而當我們在 ServiceA 上裝上一個斷路器后會怎么樣呢?

  • 斷路器在失敗次數(shù)達到一定閾值后會發(fā)現(xiàn)對 ServiceB 的請求已經無效,那么此時 ServiceA 就不需要繼續(xù)對 ServiceB 進行請求,而是直接返回失敗,或者使用其他Fallback 的備份數(shù)據(jù)。此時,斷路器處于 開路 狀態(tài)。

  • 在一段時間過后,斷路器會開始定時查詢 ServiceB 是否已經恢復,此時,斷路器處于 半開 狀態(tài)。

  • 如果 ServiceB 已經恢復,那么斷路器會置于 關閉 狀態(tài),此時 ServiceA 會正常調用 ServiceB 并且返回結果。

Node.js中斷路器機制是怎么樣的

斷路器的狀態(tài)圖如下:

Node.js中斷路器機制是怎么樣的

由此可見,斷路器的幾個核心要點如下:

  • 超時時間:請求達到多久,算引起了一次失敗

  • 失敗閾值:即斷路器觸發(fā)開路之前,需要達到的失敗次數(shù)

  • 重試超時:當斷路器處于開路狀態(tài)后,隔多久開始重新嘗試請求,即進入半開狀態(tài)

有了這些知識,我們可以嘗試創(chuàng)建一個斷路器:

class CircuitBreaker {
  constructor(timeout, failureThreshold, retryTimePeriod) {
    // We start in a closed state hoping that everything is fine
    this.state = 'CLOSED';
    // Number of failures we receive from the depended service before we change the state to 'OPEN'
    this.failureThreshold = failureThreshold;
    // Timeout for the API request.
    this.timeout = timeout;
    // Time period after which a fresh request be made to the dependent
    // service to check if service is up.
    this.retryTimePeriod = retryTimePeriod;
    this.lastFailureTime = null;
    this.failureCount = 0;
  }
}

構造斷路器的狀態(tài)機:

async call(urlToCall) {
    // Determine the current state of the circuit.
    this.setState();
    switch (this.state) {
      case 'OPEN':
      // return  cached response if no the circuit is in OPEN state
        return { data: 'this is stale response' };
      // Make the API request if the circuit is not OPEN
      case 'HALF-OPEN':
      case 'CLOSED':
        try {
          const response = await axios({
            url: urlToCall,
            timeout: this.timeout,
            method: 'get',
          });
          // Yay!! the API responded fine. Lets reset everything.
          this.reset();
          return response;
        } catch (err) {
          // Uh-oh!! the call still failed. Lets update that in our records.
          this.recordFailure();
          throw new Error(err);
        }
      default:
        console.log('This state should never be reached');
        return 'unexpected state in the state machine';
    }
  }

補充剩余功能:

// reset all the parameters to the initial state when circuit is initialized
  reset() {
    this.failureCount = 0;
    this.lastFailureTime = null;
    this.state = 'CLOSED';
  }

  // Set the current state of our circuit breaker.
  setState() {
    if (this.failureCount > this.failureThreshold) {
      if ((Date.now() - this.lastFailureTime) > this.retryTimePeriod) {
        this.state = 'HALF-OPEN';
      } else {
        this.state = 'OPEN';
      }
    } else {
      this.state = 'CLOSED';
    }
  }

  recordFailure() {
    this.failureCount += 1;
    this.lastFailureTime = Date.now();
  }

使用斷路器時,只需要將請求包裹在斷路器實例中的 Call 方法里調用即可:

...
const circuitBreaker = new CircuitBreaker(3000, 5, 2000);

const response = await circuitBreaker.call('http://0.0.0.0:8000/flakycall');

成熟的 Node.js 斷路器庫

Red Hat 很早就創(chuàng)建了一個名叫 Opossum 的成熟 Node.js 斷路器實現(xiàn),鏈接在此:Opossum 。對于分布式系統(tǒng)來說,使用這個庫可以極大提升你的服務的容錯能力,從根本上解決服務血崩的問題。

關于“Node.js中斷路器機制是怎么樣的”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。

AI