İlk WebSocket implementasyonu genellikle oldukça basittir.Client ↓ WebSocket Server
Client bağlanır.
Connection bir Map<UserId, Socket> içinde tutulur.
Mesaj geldiğinde socket’e gönderilir.
Her şey çalışır.
Sonra ikinci server instance’ını açarsınız.
Problemler orada başlar.
Kullanıcı hangi node’da?
Architecture artık şöyledir:┌─ WS Node A Client → LB ─┼─ WS Node B └─ WS Node C
Ali Node A’ya bağlı.
Ayşe Node C’ye bağlı.
Node A’nın memory’sindeki connection map Node C tarafından görülemez.
Ali Ayşe’ye mesaj göndermek istediğinde Node A’nın event’i Node C’ye ulaştırması gerekir.
Bu nedenle connection state ile domain state’i birbirinden ayırmak önemlidir.
Presence externalize edilmelidir
Örneğin Redis:presence:user:42 → node-c TTL: 30 seconds
gibi ephemeral connection metadata tutabilir.
Her node heartbeat ile presence bilgisini yeniler.
Node beklenmedik biçimde kapanırsa TTL sonunda presence kaydı temizlenir.
Bu yaklaşım tek başına bütün problemi çözmez ama node-local memory’ye olan bağımlılığı azaltır.
Fan-out
Node A’dan Node C’ye event göndermek için Redis Pub/Sub kullanılabilir:PUBLISH user:42 {...}
Node C ilgili kanalı dinler ve local socket’e mesajı iletir.
Redis de Pub/Sub’u realtime notifications, UI updates ve WebSocket node’ları arasında fan-out gibi kullanım alanları için konumlandırıyor.
Ancak önemli bir detay var.
Redis Pub/Sub at-most-once delivery sağlar. Subscriber event gönderildiği anda offline ise mesaj kaybolur. Redis’in kendi dokümantasyonu persistent/replay gereken durumlarda Streams gibi daha dayanıklı mekanizmaların değerlendirilmesini öneriyor.
Dolayısıyla:typing indicator online presence live cursor
gibi geçici event’ler için Pub/Sub gayet uygun olabilir.
Ama:financial transaction match result order completed
gibi kaybolmaması gereken olayların tek kaynağı olmamalıdır.
Reconnect normal davranıştır
Mobil ağ Wi-Fi’dan 5G’ye geçti.
Telefon ekranı kapandı.
Proxy connection’ı sonlandırdı.
WebSocket disconnect olağan durumdur.
Client reconnect ettiğinde:lastReceivedSequence = 914
gibi bir cursor gönderebilir.
Server kaçırılan durable event’leri tekrar sağlayabilir.
Bunun için mesajlarda monoton sequence veya event ID kullanmak işleri kolaylaştırır.
Backpressure
Realtime sistemlerde bir diğer kritik problem üreticinin tüketiciden hızlı olmasıdır.
Browser’ın standart WebSocket API’sinin native backpressure mekanizması olmadığını MDN özellikle belirtiyor; uygulama mesajları işleyemediğinde buffering ve memory/CPU problemleri oluşabilir.
Bu nedenle outbound queue limitleri, rate limiting, message coalescing ve düşük öncelikli event drop politikaları gerekebilir.
WebSocket ölçeklendirmek “Redis eklemek” değildir.
Gerçek problem connection ownership, transient versus durable event ayrımı, reconnect semantics ve flow control tasarlamaktır.
Tek node’da çalışan demo ile production realtime architecture arasındaki fark tam olarak burada ortaya çıkar.
WebSocket Tek Sunucuda Kolaydır: Asıl Problem İkinci Node Geldiğinde Başlar
Akif Şen
Java ve Spring ile ölçeklenebilir, yüksek performanslı ve bakımı kolay sistemler geliştiriyorum.
LinkedIn profili