Geri Dönüş Modeli: Mikro Hizmetlerde Zarif Bozulmanın Tasarlanması

Geri Dönüş Modeli: Mikro Hizmetlerde Zarif Bozulmanın Tasarlanması

Mikro hizmet mimarisinde hizmetler, dağıtılmış ağ aramalarından oluşan bir ağ oluşturur. Bu, ekiplerin hizmetleri bağımsız olarak oluşturmasına ve ölçeklendirmesine olanak tanırken, aynı zamanda sisteminizin genel güvenilirliğinin yalnızca en zayıf halkası kadar güçlü olduğu anlamına da gelir. Kritik bir hizmet çökerse veya yanıt vermez hale gelirse, tüm uygulamayı kesintiye uğratan basamaklı bir arızayı tetikleyebilir.

Tek bir bağımlılığın başarısız olduğu anda kullanıcılara genel bir “500 Dahili Sunucu Hatası” veya boş bir sayfa döndürmek, kötü bir kullanıcı deneyimidir. Bunun yerine, işler ters gittiğinde zarif bir şekilde bozulmak üzere dayanıklı sistemler inşa edilmiştir.

Geri Dönüş Modeli tam da bu noktada devreye giriyor. Birincil hizmet çağrısı başarısız olduğunda güvenli, alternatif bir yürütme yolu tanımlayarak, uygulamanızı bozulmuş durumda bile işlevsel tutabilirsiniz.

Bu kılavuzda Geri Dönüş modelini, onu uygulamaya yönelik ortak stratejileri, diğer esneklik modelleriyle nasıl etkileşime girdiğini ve Java (Resilience4j) ve Go’da geri dönüş mantığının nasıl yazılacağını inceleyeceğiz.


Gerçek Dünya Analojisi: Kahve Dükkanı Yedekleme Planı

Bir latte almak için yerel bir kafeye girdiğinizi hayal edin. Barista siparişinizi giriyor ancak kartınıza dokunduğunuzda ödeme terminalinde bir bağlantı hatası görüntüleniyor; mağazanın internet sağlayıcısında bir kesinti yaşanıyor.

Kahvehane ışıkları hemen kapatıyor, kapıları kilitliyor ve tüm müşterileri evlerine gönderiyor mu?

Tabii ki değil. Bir geri dönüş stratejisi uygularlar:

  • Ellerinde nakit varsa nakit ödeyip ödeyemeyeceğinizi soruyorlar.
  • Düzenli bir müşteriyseniz, barista adınızı ve siparişinizi bir deftere yazıp bir sonraki ziyaretinizde ödeme yapmanızı isteyebilir.
  • Kart jetonunu yerel olarak saklayan ve daha sonra internet normale döndüğünde ödemeyi işleyen bir çevrimdışı kart okuyucu kullanabilirler.
Birincil hizmet arızasını ve önbelleğe alınmış/yedek verileri almak için bir geri dönüş işleyicisine yönlendirmeyi gösteren Geri Dönüş Modeli Mimarisi Diyagramı

Yazılım tasarımında:

  • Kahve Siparişi müşteri talebidir.
  • Kart Terminali birincil aşağı yönlü hizmettir (ör. Ödeme Ağ Geçidi API’si).
  • İnternet Kesintisi bir ağ zaman aşımı veya hizmet çökmesidir.
  • Defter / Çevrimdışı Okuyucu geri dönüş yürütme yoludur.

Ortak Geri Dönüş Stratejileri

İş mantığına ve başarısız hizmetin kritikliğine bağlı olarak çeşitli geri dönüş stratejileri arasından seçim yapabilirsiniz:

1. Statik Varsayılan Değerler

En basit strateji güvenli, önceden yapılandırılmış bir statik değer döndürmektir. Bu, boş veya varsayılan verilerin görüntülenmesinin kabul edilebilir olduğu kritik olmayan özellikler için oldukça etkilidir.

  • Örnek: Profil kişiselleştirme hizmeti başarısız olursa, varsayılan avatar resmini ve genel selamlamayı döndürün.
  • Örnek: Bir öneri hizmeti başarısız olursa, hata vermek yerine boş bir liste veya evrensel en çok satanların sabit kodlu bir listesini döndürün.

2. Önbelleğe Alınmış Yanıtlar (Eski-Süre-Yeniden Doğrulama)

Canlı veriler mevcut değilse, yerel bir önbellekten veya Redis gibi hızlı dağıtılmış bir bellek deposundan salt okunur, eski verilere geri dönebilirsiniz.

  • Örnek: Bir ürün envanteri hizmeti arızalanırsa, 5 dakika öncesine ait stokta önbelleğe alınan miktarı, verilerin tam olarak güncel olmayabileceğini belirten ince bir kullanıcı arayüzü mesajıyla birlikte görüntüleyin.
  • Örnek: Bir kullanıcı ayarları hizmeti başarısız olursa, oturum açma akışını engellemek yerine önbelleğe alınan kullanıcı profilini yükleyin.

3. Alternatif Hizmet (Çoklu Sağlayıcı)

Başarılı olması gereken kritik bir işlemi yürütürken yedek olarak ikincil bir servis sağlayıcıyı yapılandırabilirsiniz.

  • Örnek: Birincil ödeme ağ geçidiniz (ör. Stripe) 5xx hatası verirse veya zaman aşımına uğrarsa geri dönüş mekanizması, işlem isteğini hemen ikincil bir ağ geçidine (ör. PayPal veya Adyen) yönlendirir.
  • Örnek: Bir coğrafi kodlama API’si başarısız olursa ikincil bir harita sağlayıcıya başvurun.

4. Daha Sonra için Sıra (Asenkron Arabellek)

Anında eşzamanlı işlem gerektirmeyen yazma işlemleri için, geri dönüş, isteği daha sonra yeniden denenmek üzere yerel bir kuyrukta veya veritabanında arabelleğe alabilir.

  • Örnek: Bir e-posta bildirim hizmeti kapalıysa, bildirim yükünü bir atılacak iletiler kuyruğuna veya yerel bir veritabanı tablosuna yazın. Bir arka plan çalışanı bu kuyruğu okuyacak ve bildirim hizmeti tekrar sağlıklı duruma geldiğinde e-postaları teslim edecektir.

Dayanıklılık Üçlüsü: Yeniden Denemeye Karşı Devre Kesiciye Karşı Geri Dönmeye Karşı

Oldukça dayanıklı bir mimari oluşturmak için Geri Dönüş modelini Yeniden Dene ve Devre Kesici modelleriyle birleştirmeniz gerekir. Üç kademeli bir savunma hattı oluştururlar:

Desen Rol Eylem Senaryo
Deseni Yeniden Dene Kısa süreli geçici aksaklıkları çözer. Kısa bir gecikmeden sonra isteği tekrarlar (geri çekilme + titreşim). Kısa ağ paketi düşer, soket sıfırlanır.
Devre Kesici Kaynak tükenmesini önler. Arızalı bir hizmete yapılan çağrıları anında engellemek için geziler açılır (başarısız). Kalıcı hizmet kesintisi, veritabanı kilitlenmeleri.
Geri Dönüş Modeli Kullanıcı deneyimini korur. Birincil çağrı başarısız olduğunda veya engellendiğinde alternatif bir eylem yürütür. Yeniden denemeler bittiğinde veya devre kesici açık olduğunda.

Birlikte Nasıl Çalışıyorlar

  1. Gelen bir istek hizmet ağ geçidine ulaşır.
  2. İstek Devre Kesici üzerinden geçer.
  3. Devre Kesici kapalıysa istek, gerçek aramayı gerçekleştiren Yeniden Dene sarmalayıcısına gider.
  4. Geçici bir hata meydana gelirse, Yeniden Dene mekanizması aramayı yeniden dener.
  5. Tüm yeniden denemeler başarısız olursa veya Devre Kesici zaten açıksa (kaynakların kaydedilmesi hızlı bir şekilde başarısız oluyorsa), Geri Dönüş İşleyicisi başarısızlığa müdahale eder ve bozulmuş/ön belleğe alınmış yanıtı döndürür.

Kod Uygulamaları

Geri dönüş mantığını hem Java’da hem de Go’da nasıl uygulayabileceğimize bakalım.

1. Java (Direnç4j)

Resilience4j, Java’da hata toleransı için endüstri standardıdır. Bir geri dönüş yöntemini, açıklamaları kullanarak bildirimsel olarak veya programlı olarak tanımlayabiliriz.

import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;

@Service
public class ProductService {

    private final InventoryClient inventoryClient;
    private final CacheManager cacheManager;

    public ProductService(InventoryClient inventoryClient, CacheManager cacheManager) {
        this.inventoryClient = inventoryClient;
        this.cacheManager = cacheManager;
    }

    // Bind this method to a circuit breaker. If it fails or is open, route to fallback
    @CircuitBreaker(name = "inventoryService", fallbackMethod = "getInventoryFallback")
    public List<String> getProductInventory(String category) {
        return inventoryClient.fetchStockByCategory(category);
    }

    // Fallback method must have the same return type and accept the same parameters,
    // plus a Throwable parameter containing the error that triggered it
    public List<String> getInventoryFallback(String category, Throwable throwable) {
        System.err.println("Primary inventory service failed: " + throwable.getMessage());
        
        // Attempt to fetch from local cache (Strategy 2)
        List<String> cachedStock = cacheManager.get("inventory:" + category, List.class);
        if (cachedStock != null) {
            return cachedStock;
        }

        // Return static empty list if cache is empty (Strategy 1)
        return Collections.emptyList();
    }
}

2. Git (Golang)

Go’da, herhangi bir yürütme işleyicisini ikincil bir işleyiciyle sarmamıza olanak tanıyan işlevsel programlama kalıplarını kullanarak temiz geri dönüş dekoratörleri yazabiliriz.

package main

import (
	"context"
	"errors"
	"fmt"
	"time"
)

// Request represents a simple payload
type Request struct {
	UserID string
}

// Response represents the returned data
type Response struct {
	Data   string
	Status string
}

// ServiceFunc represents our primary function signature
type ServiceFunc func(ctx context.Context, req Request) (Response, error)

// WithFallback wraps a service function with fallback logic
func WithFallback(primary ServiceFunc, fallback ServiceFunc) ServiceFunc {
	return func(ctx context.Context, req Request) (Response, error) {
		res, err := primary(ctx, req)
		if err != nil {
			fmt.Printf("[Warning] Primary call failed: %v. Running fallback...\n", err)
			return fallback(ctx, req)
		}
		return res, nil
	}
}

func main() {
	// 1. Define primary service that occasionally fails
	primaryService := func(ctx context.Context, req Request) (Response, error) {
		return Response{}, errors.New("database connection timeout (504)")
	}

	// 2. Define fallback service that retrieves cached data
	fallbackService := func(ctx context.Context, req Request) (Response, error) {
		// Simulating cached read
		return Response{
			Data:   fmt.Sprintf("Stale cached profile data for user %s", req.UserID),
			Status: "DEGRADED (CACHED)",
		}, nil
	}

	// 3. Wrap them together
	resilientService := WithFallback(primaryService, fallbackService)

	// 4. Execute
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()

	req := Request{UserID: "user_992"}
	response, err := resilientService(ctx, req)
	if err != nil {
		fmt.Printf("Operation completely failed: %v\n", err)
	} else {
		fmt.Printf("Response Received:\n - Status: %s\n - Data: %s\n", response.Status, response.Data)
	}
}

Geri Dönüş Modeli için En İyi Uygulamalar

  1. Geri Dönüş Yolunu Bağımlılıktan Arındırın: Geri dönüş mantığı, daha önce başarısız olan aynı alt sistemlere veya altyapıya dayanmamalıdır. Veritabanınız kapalıysa, aynı sunucudaki başka bir veritabanı sorgusuna geri dönmek de muhtemelen başarısız olacaktır.
  2. Hızlı Yürüt: Geri dönüş mantığı hızlı bir şekilde yürütülmelidir. Geri dönüş yollarınızdaki karmaşık hesaplamalardan veya yavaş iç içe çağrılardan kaçının. Amaç, kullanıcıya mümkün olan en kısa sürede yanıt vermektir.
  3. Uyarı ve İzleme: Geri dönüş yürütmelerini her zaman günlüğe kaydedin ve telemetri sayaçlarını artırın. Sisteminiz geri dönüş yollarını yürütüyorsa bu, sistemin bozulmuş bir durumda çalıştığı anlamına gelir. Geri dönüş oranlarının normal eşikleri aştığını bilmek için uyarılara ihtiyacınız var.
  4. Kullanıcı Arayüzünü Dayanıklı Hale Getirin: Kullanıcı arayüzünün, istemci tarafı düzenini bozmadan yedek yükleri (boş koleksiyonlar veya kısmi durumlar gibi) kabul edecek şekilde tasarlandığından emin olmak için ön uç mühendisleriyle yakın işbirliği içinde çalışın.

Çözüm

Geri Dönüş Modeli, mikro hizmet güvenilirliği için nihai sigorta poliçesidir. Arızaları önceden tahmin ederek ve zarif bir geri dönüş mantığı sağlayarak, sert çökmeleri ince, yönetilebilir kesintilere dönüştürürsünüz. Bu modeli Yeniden Denemeler ve Devre Kesiciler ile birleştirmek, uygulamanızın kullanıcılara hizmet vermeye devam ederken büyük harici kesintilere dayanabilmesini sağlar.