天天看點

寫好operator之重試控制政策

作者:雲原生驿站

Kubernetes 提供了一些控制重試政策來處理與 API 互動時可能發生的錯誤和故障。這些政策可以幫助您實作可靠的操作,提高應用程式的容錯性。

  1. 重試:
    • Retry

      政策在操作失敗時會立即進行重試。
    • 控制器或用戶端可以指定重試次數,如果操作在重試次數内仍然失敗,則放棄重試并傳回錯誤。
  2. 指數退避(Exponential Backoff):
    • Exponential Backoff

      政策會根據重試次數逐漸增加重試的時間間隔。
    • 控制器或用戶端可以指定初始的重試間隔和最大的重試間隔。
    • 重試間隔會根據指數函數逐漸增加,例如每次重試的間隔時間是前一次的兩倍。
    • 這樣的政策有助于避免在操作失敗時産生過多的請求,減少對系統的負載壓力。
  3. 等待(Wait):
    • Wait

      政策會在操作失敗時等待一段固定的時間間隔,然後再進行重試。
    • 控制器或用戶端可以指定等待的時間間隔。
package main

import (
"fmt"
"math/rand"
"time"

"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/util/retry"
"k8s.io/klog/v2"
)

var numberOfRetries = 0

func main() {
// 建立一個自定義的退避政策

 waitBackoff := wait.Backoff{
 Duration: 2 * time.Second, // 初始退避間隔
 Factor: 2, // 退避系數
 Jitter: 0.1, // 随機化因子
 Steps: 10, // 最大重試次數
 }

// 自定義的退避政策函數
 backoffFunc := func() error {
 klog.Infof("Performing operation...")
 err := performOperation()
if err != nil {
 klog.Warningf("Operation failed: %v", err)
return err
 }
 klog.Infof("Operation succeeded.")
return nil
 }

// 使用自定義的退避政策執行操作
 err := retry.OnError(waitBackoff, shouldRetry, backoffFunc)
if err != nil {
 klog.Errorf("Failed to perform operation: %v", err)
 } else {
 klog.Infof("Operation completed successfully.")
 }
}

// 執行操作的函數
func performOperation() error {
 numberOfRetries++
 klog.Infof(fmt.Sprintf("Start %d retry", numberOfRetries))
// 模拟操作失敗的情況
if rand.Intn(10) < 9 {
return fmt.Errorf("Operation failed")
 }
return nil
}

// 判斷是否應該重試的函數
func shouldRetry(err error) bool {
// 這裡可以根據具體的錯誤類型進行判斷
return true
}
           

使用

wait.Backoff{}

結構體時,各個字段的含義如下:

  • Duration: 2 * time.Second

    :初始退避間隔是 2 秒。在進行第一次重試之前,我們會等待 2 秒鐘,這是重試的初始等待時間。
  • Factor: 2

    :退避系數是 2。每次重試時,我們會将上一次的等待時間乘以 2,以獲得下一次重試的等待時間。例如,如果第一次重試等待了 2 秒,那麼下一次将等待 4 秒,再下一次将等待 8 秒,依此類推。
  • Jitter: 0.1

    :随機化因子是 0.1。為了避免所有重試操作同時發生,我們可以在每次計算等待時間時引入一定的随機性。随機化因子表示在計算等待時間時,我們會将結果乘以一個介于 0 和 1 之間的随機值。這樣可以使得每個重試操作之間有一定的随機差異。
  • Steps: 5

    :最大重試次數是 5。我們會在達到最大重試次數後停止重試操作。在這種情況下,即使上一次重試失敗,我們也不會再進行下一次重試。

假設我們有一個需要重試的操作。初始退避間隔為 2 秒,然後每次重試的等待時間都會翻倍。為了避免同時進行所有重試操作,我們會在每次計算等待時間時引入一定的随機性。最大重試次數為 5,如果達到最大重試次數後仍然失敗,我們将停止重試。

如果想提高模拟測試的時候,程式在中間成功的機率的話,可以修改這裡的值
if rand.Intn(10) < 9 {
return fmt.Errorf("Operation failed")
}
           

除了

retry.OnError

還有

retry.RetryOnConflict

RetryOnConflict

函數的作用是在發生沖突錯誤(Conflict Error)時進行重試。

函數接受兩個參數:

  • backoff

    :一個實作了

    wait.Backoff

    接口的對象,用于定義最大重試次數和兩次重試之間的等待間隔。
  • fn

    :一個函數,表示要執行的操作函數。

在函數内部,

RetryOnConflict

調用了

OnError

函數,并傳入了

errors.IsConflict

函數作為

retriable

參數。

errors.IsConflict

是一個用于判斷錯誤是否為沖突錯誤的函數。

OnError

函數會在每次重試時調用

fn

函數執行操作,并根據傳回的錯誤判斷是否可重試。而将

errors.IsConflict

作為

retriable

參數,意味着隻有當傳回的錯誤是沖突錯誤時,才會進行重試。

通過使用

RetryOnConflict

函數,我們可以在執行某個操作時遇到沖突錯誤時自動進行重試,直到操作成功或達到最大重試次數。這樣可以處理并發通路資源時可能發生的沖突情況,提高操作的成功率。

何為Conflict Error?

在Kubernetes中,沖突錯誤(Conflict Error)通常指的是針對資源對象的更新操作時發生的沖突情況。

當多個并發的操作嘗試修改同一個資源對象時,可能會發生沖突。例如,兩個操作同時嘗試更新同一個資源的字段,或者一個操作在另一個操作完成之前修改了資源的狀态。

當這種沖突發生時,Kubernetes會傳回一個沖突錯誤。這個錯誤通常是HTTP狀态碼為 409(Conflict)的響應。它表示目前的操作與其他操作存在沖突,無法順利執行。

沖突錯誤的常見原因包括:

  • 資源版本不比對:當多個操作使用相同的資源版本号來修改同一個資源時,會導緻沖突。
  • 并發更新:當多個操作同時嘗試更新同一個資源的字段或狀态時,可能會發生沖突。
  • 樂觀并發控制:某些資源對象使用樂觀并發控制機制,即在更新時會比較資源的版本号,如果版本号不比對,則表示發生了沖突。

繼續閱讀