Portal/Notes πŸ“
Programming

Circuit Breaker

Pola circuit breaker untuk menangani kegagalan layanan jarak jauh β€” mencegah cascading failure dengan tiga state: Closed, Open, Half-open.

Versi Bahasa Indonesia. Baca dalam Bahasa Inggris

Latar Belakang

  • Ada masalah ketika panggilan ke layanan jarak jauh gagal biasanya karena jaringan lambat, timeout, dll.
  • Masalah ini biasanya diperbaiki dengan strategi retry pattern di mana pemanggil akan mencoba kembali memanggil layanan jarak jauh (dapat menambahkan backoff juga).
  • Tetapi terkadang gagal karena sesuatu yang membutuhkan waktu lebih lama untuk diperbaiki.
  • Maka retry pattern di sini akan sia-sia karena akan menghabiskan sumber daya pemanggil.
  • Selain itu jika layanan sangat sibuk, ini dapat menciptakan cascading failure.
  • Contoh:
    • Request masuk dari pengguna ke layanan kita.
    • Kemudian layanan kita perlu melakukan request eksternal ke API pihak ketiga.
    • Tetapi karena suatu masalah, request timeout setelah 10s.
    • Responsnya timeout dan kita memiliki retry pattern yang akan mencoba ulang request maksimal 5 kali.
    • Ini akan membuat request pengguna macet setidaknya selama 50s.
    • Bayangkan jika layanan kita sangat sibuk dan menangani ribuan request setiap detik.
    • Ini dapat menyebabkan sumber daya terkunci oleh setidaknya 50.000 request yang dapat menyebabkan cascading failure.

Solusi

Circuit Breaker bertindak sebagai proxy untuk aplikasi yang mungkin gagal. Proxy harus memantau jumlah kegagalan terbaru yang terjadi. Dan menggunakan informasi itu untuk memutuskan apakah akan mengizinkan operasi atau langsung mengembalikan error.

3 Status dari circuit breaker pattern:

  • Closed: Status normal, di mana semua request diizinkan melewati ke layanan.
  • Open: Circuit breaker menolak semua request ke layanan untuk mencegah kegagalan lebih lanjut.
  • Half-open: Circuit breaker mengizinkan sejumlah terbatas request untuk melewati ke layanan untuk menguji apakah sudah berfungsi kembali.

Manfaat:

  • Dapat mencegah aplikasi berulang kali mencoba mengeksekusi operasi yang kemungkinan akan gagal.
  • Memungkinkan untuk melanjutkan tanpa menunggu.
  • Mencegah pemborosan sumber daya CPU dan Memori.

Contoh

Menggunakan Library Sony

Berikut adalah contoh penggunaan circuit breaker dari library populer sony/gobreaker.

package main

import (
    "fmt"
    "log"
    "time"

    "github.com/sony/gobreaker/v2"
)

// Simulasi layanan eksternal
func callExternalService() (string, error) {
    // Simulasi kegagalan acak
    if time.Now().Unix()%2 == 0 {
        return "", fmt.Errorf("service unavailable")
    }
    return "Success: Data retrieved", nil
}

// Bungkus panggilan layanan eksternal dengan Circuit Breaker
func externalServiceWithCircuitBreaker(cb *gobreaker.CircuitBreaker[string]) (string, error) {
    // Eksekusi fungsi yang dilindungi circuit breaker
    result, err := cb.Execute(func() (string, error) {
        return callExternalService()
    })
    if err != nil {
        return "", err
    }
    return result, nil
}

func main() {
    // Konfigurasi Circuit Breaker
    cbSettings := gobreaker.Settings{
        Name:        "ExternalServiceCB",
        MaxRequests: 3,                // Izinkan 3 request di half-open state
        Interval:    10 * time.Second, // Reset state setelah 10 detik
        Timeout:     5 * time.Second,  // Waktu tunggu sebelum transisi ke half-open
        ReadyToTrip: func(counts gobreaker.Counts) bool {
            // Buka circuit ketika kegagalan melebihi 50% dari total request
            failureRatio := float64(counts.TotalFailures) / float64(counts.Requests)
            return counts.Requests >= 5 && failureRatio >= 0.5
        },
    }
    circuitBreaker := gobreaker.NewCircuitBreaker[string](cbSettings)

    // Simulasi panggilan periodik ke layanan eksternal
    for i := 0; i < 10; i++ {
        result, err := externalServiceWithCircuitBreaker(circuitBreaker)
        if err != nil {
            log.Printf("Request %d: Circuit Breaker Triggered - %v", i+1, err)
        } else {
            log.Printf("Request %d: %s", i+1, result)
        }
        time.Sleep(1 * time.Second)
    }
}

Edit on GitHub

Last updated on

Circuit Breaker | Faisal Affan