首頁  >  文章  >  後端開發  >  Go 應用程式中的斷路器

Go 應用程式中的斷路器

王林
王林原創
2024-09-03 14:15:11974瀏覽

如今,我們的應用程式具有多個相依性是很常見的,尤其是在微服務環境中工作時。當我們的應用程式報告錯誤時,我們發現一個依賴項已關閉,這種情況並不罕見。

提高我們恢復能力的一個好習慣是關閉與那些表現不佳的應用程式的溝通。看看其他領域,我們從電氣工程中學到了斷路器的概念,即當發生故障時開關會關閉。在巴西,所有房屋都有這些開關,如果我們的電網不穩定,這些開關會自動關閉。

在電腦科學中,我們的斷路器有點複雜,因為它也有一個中間狀態。下圖詳細解釋了它的工作原理:

Circuit Breaker in Go apps

簡而言之,可能的狀態有:

  • open:應用程式之間沒有通訊。當達到此狀態時,計時器啟動,允許重新建立依賴關係。當計時器結束時,我們將進入半開狀態。
  • 關閉:應用程式之間存在通訊。每次請求失敗時,都會更新計數器。如果達到故障閾值,我們會將電路移至開路狀態。
  • 半開:這是一種治癒狀態,直到我們可以照常工作。在此過程中,如果我們達到成功閾值,我們就會關閉。如果請求仍然失敗,我們將重新開放。

很酷,對吧?為了更好地解釋這個概念,為什麼不創建一個呢?

首先,讓我們建構我們的服務A。它將負責接收所有請求,換句話說,它將是我們的主應用程式所依賴的服務。為了簡化,我們將公開兩個端點:始終響應 200 的 /success 和始終響應 500 的 /failure。

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/success", func(w http.ResponseWriter, r *http.Request) { 
    w.WriteHeader(http.StatusOK) })
    http.HandleFunc("/failure", func(w http.ResponseWriter, r *http.Request) { 
    w.WriteHeader(http.StatusInternalServerError) })

    fmt.Println("Server is running at http://localhost:8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

我們的服務 B 將負責呼叫服務 A 並建立我們的斷路器。 Go 社群已經擁有已經實現了該模式的 lib gobreaker。首先,我們定義斷路器屬性:

var st gobreaker.Settings
st.Name = "Circuit Breaker PoC"
st.Timeout = time.Second * 5
st.MaxRequests = 2
st.ReadyToTrip = func(counts gobreaker.Counts) bool {
    return counts.ConsecutiveFailures >= 1
}

即使該函式庫允許我們自訂更多屬性,我們只會專注於三個:

  • 超時:處於開啟狀態的時間長度。在此範例中,我們選擇五秒。
  • MaxRequests:關閉之前有多少個成功請求。在此範例中,我們決定了兩個請求。
  • ReadyToTrip:定義從關閉狀態變成開啟狀態的條件。簡單來說,一次失敗就夠了。

現在我們只需啟動斷路器並發送請求:

cb := gobreaker.NewCircuitBreaker[int](st)

url := "http://localhost:8080/success"
cb.Execute(func() (int, error) { return Get(url) })
fmt.Println("Circuit Breaker state:", cb.State()) // closed!

url = "http://localhost:8080/failure"
cb.Execute(func() (int, error) { return Get(url) })
fmt.Println("Circuit Breaker state:", cb.State()) // open!

time.Sleep(time.Second * 6)
url = "http://localhost:8080/success"
cb.Execute(func() (int, error) { return Get(url) })
fmt.Println("Circuit Breaker state:", cb.State()) // half-open!

url = "http://localhost:8080/success"
cb.Execute(func() (int, error) { return Get(url) })
fmt.Println("Circuit Breaker state:", cb.State()) // closed!

我們可以注意到 gobreaker 的工作原理就像函數的包裝器。如果函數傳回錯誤,則會增加失敗計數器,如果沒有回傳錯誤,則會增加成功計數器。讓我們定義該函數:

func Get(url string) (int, error) {
    r, _ := http.Get(url)

    if r.StatusCode != http.StatusOK {
        return r.StatusCode, fmt.Errorf("failed to get %s", url)
    }

    return r.StatusCode, nil
}

這就是我們如何擁有一個帶有斷路器的 Go 應用程式!使用此模式時,您可以透過提高應用程式對依賴項故障的容忍度來提高應用程式的彈性。此外,使用這個函式庫消除了大部分複雜性,使得我們更容易在日常應用程式中採用該模式。如果您想查看此概念驗證的程式碼,請在此處查看。

如果您仍然對其他彈性模式感到好奇,Elton Minetto 也發表了一篇關於它的精彩部落格文章!

您也可以在我的個人部落格上查看這篇文章和其他貼文。請在評論中告訴我您對這篇文章的看法,並提出一個問題:您以前使用過斷路器嗎?

以上是Go 應用程式中的斷路器的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn