Akış işleme mimarileri, Apache Storm'un en az bir kez işleme modelinden, Apache Flink ve Spark Structured Streaming tarafından tanıtılan modern tam bir kez garantilerine evrildi. Şirketler, toplu Lambda mimarilerinden sürekli Kappa akışlarına geçtikçe, karmaşıklık basit dönüşümlerden, pencereleme toplama ve oturumlaştırma için dağıtık durumu yönetmeye kaydı. Veri egemenliği gerekliliklerinin ve bölgesel gecikme kısıtlamalarının doğuşu, paylaşılan NFS veya SAN depolama gerektirmeden aktif-aktif dağıtımlar gerektirdi ve coğrafi arıza durumlarında durum tutarlılığı için yeni zorluklar yarattı.
Durumsal akış işleme, işleme düğümlerinde (anahtar pencereleme, oturum depoları) yerel olarak gigabaytlarca işletim durumunu korumayı gerektirirken, saniyede milyonlarca olayı almayı da gerektirir. Tam bir kez anlamı, üç bileşen arasında atomik taahhütleri gerektirir: kaynak yerleşim izleme, durum arka uç güncellemeleri ve çıkış yazıları. Paylaşılan depolama olmadan bölge arası aktif-aktif çoğaltma, ağ bölünmeleri gerçekleştiğinde bölünmüş zihin riski oluştururken, otomatik ölçeklendirme, akış halindeki kayıtları düşürmeden canlı durum göçü gerektirir veya işleme zamanı garantilerini ihlal etmemelidir. Çoklu dillerin desteklenmesi (Java, Python, Go), geleneksel olarak serileştirme aşamasını zorunlu kılar veya dil-spesifik çalışma zamanı kilitleyebilir.
Mimari, Apache Kafka veya Apache Pulsar’ı birleşik günlük olarak kullanan ayrık bir tasarım içerir, işleme düğümleri Kubernetes üzerinde çalışırken dil bağımsız gRPC yan kuruluşları çoklu dil desteği sağlar. Durum yönetimi, S3 uyumlu nesne depolama ile koordine edilen asenkron artımlı kontrol noktaları ile yerleşik RocksDB kullanır. Tam bir kez anlamı, durum için Chandy-Lamport anlık görüntü algoritması ve işlemelere yönelik iki aşamalı taahhüt (2PC) protokolleri ile sağlanır (Kafka işlemleri veya idempotent JDBC yazıları). Bölge arası çoğaltma, kütük tabanlı durum nakli kullanılarak Kafka MirrorMaker 2 veya Pulsar Geo-Replication ile gerçekleştirilir ve toplama işlemleri için CRDT-tabanlı birleşik sayaçlar ve anahtar durum için sürümlü birincil sahiplik aracılığıyla çatışma çözümü sağlanır.
Platform, dört mantıksal katmandan oluşur: alma, işleme, durum yönetimi ve koordinasyon.
Alma Katmanı
Apache Kafka kümeleri, MirrorMaker 2 ile iki yönlü konu çoğaltımını sağlamak için birden fazla bölgede çalışır. Üretici idempotansı ve işlem kimlikleri, üretici arızası sırasında bile tam bir kez alma sağlar.
İşleme Katmanı
Apache Flink veya benzer akış işleyicileri, Kubernetes Durumlu Setleri olarak çalışır. Her TaskManager, Protobuf ile serileştirilmiş görevleri kabul eden bir gRPC yan kuruluşu açar ve bu da Python ve Go kullanıcı tanımlı işlevlerinin (UDF) gRPC konteynerleri içinde çalışmasına olanak tanırken, Java çalışma zamanı durumu ve kontrol noktasını yönetir. JobManager, kayıt anahtarları üzerinde tutarlı karma kullanarak topolojiyi TaskManagers arasında paylaştırır.
Durum Yönetimi
Operatör durum arka uçları RocksDB kullanır ve artımlı kontrol noktalarını etkinleştirir. Kontrol noktaları delta durum değişikliklerini bölgesel S3 havuzlarına her 15 saniyede bir asenkron olarak yazar. Bölge arası tutarlılık için, aktif-aktif dağıtımlar, monotonik toplama (sayım, toplam) için LWW-Element-Set CRDT'leri ve birleşik işlemler için birincil anahtar bağlılığı kullanır. Bölgesel bir arıza durumunda, hazır bekleyen TaskManagers, durumlarını S3'ten Savepoints kullanarak yeniden doldurur.
Tam Bir Kez Garantileri
Sistem, tam bir kez garantilerini sonlandırmak için aşağıdaki yöntemleri uygular:
Küresel bir araç paylaşım platformu, AWS us-east-1 ve AWS eu-west-1 üzerinde sürücü kullanılabilirliği ve yolculuk talebi verilerini geohash gibi birleştirerek, gerçek zamanlı dalgalanma fiyatlandırma hesaplamaları gerektirdi. Önceki mimari, çoğaltma gecikmesi ile tek bir birincil Redis kümesi kullanıyordu ve bu durum, fiyattaki hesaplamaların bölgesel kesintiler sırasında, eski veya çoğaltılmış dalgalanma çarpanları üretmesine neden olan 2 saniyelik arıza pencerelerine yol açtı ve bu da yanlış ücret hesaplamalarına ve müşteri şikayetlerine yol açtı.
Çözüm 1: Aktif-Pasif ve Paylaşılan Depolama
Ekip, durum depolaması için bölgeler arası paylaşılan EFS (paylaşılan NFS) bağlamayı düşündü. Artılar: Tek yazar semantiği ile failover süreçlerini basitleştirdi, güçlü tutarlılık sağladı. Eksiler: EFS’nin gecikmesi, bölge arası erişimde 100ms'yi geçti ve 50ms işleme SLA'sını ihlal etti; ek olarak, NFS yazma tutarlılığı sorunları, ağ bölünmeleri sırasında kontrol noktası bozulmalarına yol açtı.
Çözüm 2: Lambda Mimarisi
Düzeltmeler için Kafka Streams ile bir hız katmanı ve Spark ile bir toplu katmanı uygulamak. Artılar: Değişmez günlükler sayesinde hata toleransı, basit kurtarma. Eksiler: İki kod yolu sürdürmenin operasyonel karmaşıklığı; toplama düzeltmeleri, dalgalanma fiyatlandırmasının alt saniye doğruluğu gerektirdiği için çok geç geldi.
Çözüm 3: Aktif-Aktif Akış İşleme ile CRDT'ler
Her iki bölgede de Apache Flink dağıtarak, RocksDB durumu, artımlı S3 kontrol noktaları ve yolculuk sayımları için CRDT tabanlı sayaçlar ile dağıtım gerçekleştirdi. Artılar: Yerel işleme gecikmesi 20ms'nin altında, eşzamanlı bölgesel güncellemeler için otomatik çatışma çözümü, sıfır-duraklama failover. Eksiler: Toplamaların birleşik olabilmesi için yeniden yapılandırma gerektirdi (G-Counters ve PN-Counters kullanılarak) ve çift bölgesel kontrol noktaları için depolama maliyetlerini artırdı.
Ekip, iş gereksinimi olan %99,99 kullanılabilirlik ve alt saniyelik failover talebinin, Çözüm 1'in 2 saniyelik zaman dilimini veya paylaşılan depolamanın gecikmesini tolere edemediği için Çözüm 3'ü seçti. Sürücü sayımları için G-Counters ve son fiyatlandırma çarpanları için LWW-Registers uyguladılar.
Sonuç
Sistem, her iki bölgede de 15ms p99 gecikme ile tam bir kez dalgalanma fiyatlandırma hesaplamaları gerçekleştirdi. Simüle edilmiş bir us-east-1 kesintisi sırasında, eu-west-1, yerel olarak çoğaltılmış durumu kullanarak sorunsuz bir şekilde işleme devam etti ve çoğaltılmış ücret hesaplamaları üretmedi. Kontrol noktası kurtarma süresi ortalama 800ms idi ve bu da alt saniye gereksinimlerinin çok altında kaldı.
Durumsal akış işlemcilerinde kontrol noktası aralığı ayarlamasının geri basınç mekanizmalarıyla etkileşimi nedir?
Birçok aday, kurtarma süresi için kontrol noktası aralıklarını optimize ederken, geri basıç yayılımını dikkate almadı. Kontrol noktası engelleri geri basınç nedeniyle yavaş hizalandığında, Chandy-Lamport algoritması, boru hattı yürütmesini duraklatır ve bu da muhtemel zincirleme zaman aşımlarına yol açar. Doğru yaklaşım, kontrol noktası zaman aşım sürelerini geri basınç eşiklerine hizalamak, yüksek yük sırasında hizalanmamış kontrol noktası (engellerin tamponları geçmesine neden olduğu) kullanmak ve senkron ile asenkron kontrol noktası aşamaları arasında ayrım yapmaktır. RocksDB'nin artımlı kontrol noktaları, SST sıkıştırmasının disk G/Ç'yi aşırı yükleyerek geri basıncı artırmasını engellemek için RateLimiter yapılandırmaları kullanılarak kısıtlanmalıdır.
Kimliksizlik dışındaki teslimat ile tam bir kez işleme semantiği arasındaki temel fark nedir?
Idempotent çıkışlar, çoğaltılan işleme ile aynı çıktı durumunu garanti eder (örneğin, PostgreSQL veya HBase üzerindeki UPSERT işlemleri), ancak yeniden denemeler sırasında ara durumları açığa çıkarır. Eğer bir çıkış, kayıtları A, B yazar, sonra çökme meydana gelir ve A, B, C yazar, aşağı akıştaki gözlemciler, önceden çoğaltma sonucunda A, B, A, B, C gibi anlık olarak görmekte sorun yaşayabilir. Tam bir kez işleme, işlem izolasyonu kullanır; önceden taahhüt edilen veriler, kontrol noktası tamamlanana kadar görünmez kalır. Bu, çıkışın işlemleri desteklemesini gerektirir (örneğin, izole.seviyesi=okunan_taahhütlü Kafka işlemleri) veya iki aşamalı taahhüt protokollerini gerektirir. Adaylar genellikle, kimliksizlik farkındalığı sorununu çözerken, kurtarma sırasında tutarlılık/görünürlük sorununu göz ardı ettiklerini unuturlar.
Olay-zamanlı pencereleme, bölge arası arıza senaryolarında geç gelen verileri nasıl ele almalıdır?
Bölge A'dan Bölge B'ye failover gerçekleştiğinde, Bölge A'nın ağ tamponlarındaki akış halindeki kayıtlar kaybolabilir veya su seviyesinin ufku dışında geç kalabilir. Adaylar sıklıkla su seviyelerini sınırsız bir şekilde uzatmayı önerir, bu da pencere tamlığı garantilerini bozabilir. Doğru mimari, geç verileri toplama için Yan Çıkışlar (Flink terminolojisinde) kullanmak ve İzin Verilen Geçlik tanımlarıyla birleştirmek üzere tasarlanmıştır. Failover sırasında sistem, zaman damgaları ile S3 Savepoints'ten pencereleri doldurmalı, ardından başarısız bölgenin ölü mektup kuyruğundan geç gelen kayıtları sonraki pencerelere birleştirmeli veya belirli geç veri işleyicilerini tetiklemesi gerekir. Ek olarak, su seviyesi oluşturma süreçleri, bölgeler arasında idempotent olmalıdır; su seviyelerini tavan saatine dayandırmak, failover sırasında farklılık yaratır. Bu nedenle, su seviyeleri, her iki aktif bölgedeki monoton olay-zamanlı çıkışın türetilmiş olması gerekir.