System Design

Sınırların Ötesine Ölçekleme: Hata Toleranslı Dağıtık Sistemler Tasarlamak

Yazılım ürünleri niş araçlardan küresel platformlara evrildikçe, başlangıçtaki monolitik mimari genellikle bir darboğaza dönüşür. Milyonlarca eşzamanlı kullanıcıyı, petabaytlarca veriyi ve sıfır kesinti gereksinimlerini karşılamak, mühendislerin temel tasarım ilkelerini yeniden düşünmesini zorunlu kılar. Ölçeklenebilirlik yalnızca daha fazla CPU döngüsü eklemekle ilgili değildir; durum yönetimi, ağ gecikmesi ve arıza alanlarını içeren karmaşık bir mimari zorluktur. Bu gönderi, aşırı yük altında dayanıklılığını koruyan sistemler inşa etmek için pratik kalıplar sunarak dikey ve yatay ölçeklemenin çift yaklaşımlarını ele alır.

Dikey ve Yatay Ölçekleme: Doğru Yolu Seçmek

Dikey ölçekleme veya "yukarı ölçekleme", tek bir düğümün kaynaklarını artırmayı içerir: daha fazla RAM, daha hızlı CPU'lar veya daha büyük diskler. Uygulanması kolaydır ancak donanım sınırları tarafından içsel olarak sınırlıdır ve tek bir arıza noktası oluşturur. Sunucu çökerse, tüm hizmet durur. Bu yaklaşım, veri tutarlılığının kritik olduğu ve yükün güçlü tek bir örnek tarafından yönetilebildiği durumlu uygulamalar için uygundur.

Yatay ölçekleme veya "dışa ölçekleme", sisteme daha fazla düğüm ekler. Bu, bileşenlerin durumsuz olması veya durumun verimli bir şekilde paylaşılabilmesini gerektiren temel olarak farklı bir mimari gerektirir. Birincil avantajı hata toleransıdır; bir düğüm arızalanırsa, diğerleri yükü ele alabilir. Ancak durum senkronizasyonu, yük dengeleme ve ağ iletişimi konusunda karmaşıklık getirir. Modern dağıtık sistemlerin çoğu için, web'e yönelik hizmetlerde yatay ölçekleme varsayılan stratejidir.

Yatay Ölçeklenebilirlik İçin Tasarım

Yatay olarak ölçeklemek için bileşenler arasındaki sıkı bağımlılığı ortadan kaldırmalıyız. En etkili strateji, mikroservisler veya modüler monolitler kullanarak endişeleri ayırmaktır. Her hizmet, belirli yük profiline göre bağımsız olarak dağıtılabilir ve ölçeklenebilir olmalıdır. Bu mimarideki kritik bir bileşen, gelen trafiği birden fazla sunucu örneği arasında dağıtan Yük Dengeleyicisidir.

Özel bir vekil katmanında basit bir sırayla dönüşümlü (round-robin) yük dengeleme mantığı için aşağıdaki sözde kodu düşünün:

class LoadBalancer {
    private List<String> servers;
    private int currentIndex = 0;

    public String getNextServer() {
        if (servers.isEmpty()) {
            throw new NoAvailableServerException();
        }
        
        String server = servers.get(currentIndex);
        currentIndex = (currentIndex + 1) % servers.size();
        
        // Basic health check logic
        if (!healthCheck(server)) {
            return getNextServer(); // Recursively find next healthy node
        }
        
        return server;
    }
    
    private boolean healthCheck(String server) {
        // Ping or check status endpoint
        return ping(server) > 0;
    }
}

Üretim ortamında, Nginx, HAProxy veya AWS ALB veya Kubernetes Ingress Controllers gibi bulut yerli çözümler gibi özel yük dengeleyicilere güveneceksiniz. Bu araçlar gelişmiş sağlık kontrolleri, yapışkan oturumlar ve SSL sonlandırması sağlar.

Durumsuz Bir Dünyada Durumu Yönetme

Sunucular yerel durum tuttuğunda yatay ölçekleme bozulur. Kullanıcı A, Sunucu 1'de oturum açarsa ve sonraki isteği Sunucu 2'ye ulaşırsa, Sunucu 2 Kullanıcı A'nın kim olduğunu bilemez. Bunu çözmek için durumu dışsallaştırırız.

1. Oturumlar: Oturum verilerini Redis veya Memcached gibi dağıtık bir önbelleğe taşıyın. Belirli bir sunucuya işaret eden bir oturum kimliğini çereze (cookie) kaydetmek yerine, çerez, paylaşılan önbellekteki bir anahtara atıfta bulunan bir jeton içerir.

2. Veri Kalıcılığı: Birden fazla düğüm arasında yazma ve okuma ölçeklemesini ele alabilen dağıtık veritabanlarını (Cassandra, DynamoDB) veya parçalanmış ilişkisel veritabanlarını (CockroachDB, Vitess) kullanın.

3. İleti Kuyrukları: Asenkron işleme için, üreticileri ve tüketicileri ayırmak için Kafka veya RabbitMQ kullanın. Bu, tüketici gruplarını geri bildirim boyutuna göre bağımsız olarak ölçeklemenizi sağlar.

Hata Toleransı ve Dayanıklılığı Sağlama

Dağıtık sistemlerde arıza bir istisna değil; bir sabittir. Bileşenler zaman aşımına uğrayacak, ağ bölünmeleri oluşacak ve düğümler çökecektir. Sağlam bir sistem, bu arızaları varsayar ve onlara göre tasarlanır.

Devre Kesiciler (Circuit Breakers): Hystrix veya Resilience4j devre kesici gibi kalıpları uygulayın. Aşağı akıştaki bir hizmet sürekli olarak başarısız olursa, devre kesici açılır, daha fazla çağrıyı engeller ve bir yedek yanıt döndürür. Bu, yavaş bir bağımlılığın tüm sistemi aşağı çektiği kaskat arızalarını önler.

Üstel Geri Çekme ile Yeniden Denemeler: Bir istek başarısız olduğunda, hemen yeniden denemek, toparlanan sistemi aşırı yükleyebilir. Bunun yerine, yeniden denemeleri aralıklamak ve yük sıçramasını azaltmak için jitter ile üstel geri çekme kullanın.

Sağlık Kontrolleri: Hizmetlerin sağlıklı olduğundan emin olmak için düzenli olarak sorgulayın. Kubernetes gibi orkestrasyon araçları, başarısız konteynerleri otomatik olarak yeniden başlatır ve sağlıksız düğümleri rotasyondan çıkarır.

Pratik Örnek: Dosya Yükleme Hizmetini Ölçekleme

Hayal edin, bir fotoğraf paylaşım uygulaması. Kullanıcılar görüntüleri yükler, bunlar işlenir, yeniden boyutlandırılır ve saklanır. Bunu nasıl ölçekleriz:

  1. Alım (Ingestion): Kullanıcılar, önceden imzalanmış bir URL üzerinden doğrudan bir İçerik Dağıtım Ağına (CDN) veya nesne depolamaya (S3) yükler. Bu, ağır I/O'yu uygulama sunucularından alır.
  2. Bildirim: S3, yeni bir dosya yüklendiğinde bir ileti kuyruğuna (Kafka) bir bildirim olayı gönderir.
  3. İşleme: Durumsuz işçilerden oluşan bir filo bu olayları tüketir. Her işçi dosyayı indirir, işler (yeniden boyutlandırma, filtreleme) ve varyantları yükler. İşçiler durumsuz olduğu için, kuyruk derinliğine bağlı olarak 10 veya 1000 tanesini başlatabiliriz.
  4. Meta Veri: Ayrı bir API hizmeti, parçalanmış bir veritabanında meta veri aramalarını ve güncellemelerini ele alır.

Bu mimari, alım, işleme ve meta veri katmanlarının bağımsız olarak ölçeklenmesine olanak tanır. Yükleme hacmi sıçrarsa, CDN ve kuyruğu ölçeklersiniz. İşleme geride kalırsa, işçileri ölçeklersiniz. Meta veri sorguları yavaşsa, veritabanı kopyalarını ölçeklersiniz.

Sonuç

Ölçeklenebilir dağıtık sistemler tasarlamak, "tek bir makineyi hızlandırmaktan" "birçok makineyi güvenilir bir şekilde orkestramaya" zihniyet değişikliği gerektirir. Dikey ölçekleme sadelik sunarken, yatay ölçekleme modern uygulamalar için gereken esnekliği ve dayanıklılığı sağlar. Durumsuzluğu benimseyerek, yük dengeleyicilerden yararlanarak, dağıtık veri depolarını kullanarak ve devre kesiciler gibi sağlam hata toleransı kalıplarını uygulayarak, yalnızca büyümeyi ele almakla kalmayıp kaçınılmaz arızalar karşısında gelişen sistemler inşa edebilirsiniz. Küçük başlayın, ancak ilk günden itibaren ölçeklemeyi göz önünde bulundurarak mimari tasarlayın.

Share: