{"id":27,"date":"2026-09-23T20:54:26","date_gmt":"2026-09-23T20:54:26","guid":{"rendered":"https:\/\/www.akifsen.com.tr\/blog\/?p=27"},"modified":"2026-09-23T20:54:26","modified_gmt":"2026-09-23T20:54:26","slug":"websocket-scaling-redis-presence-realtime","status":"publish","type":"post","link":"https:\/\/www.akifsen.com.tr\/blog\/2026\/09\/23\/websocket-scaling-redis-presence-realtime\/","title":{"rendered":"WebSocket Tek Sunucuda Kolayd\u0131r: As\u0131l Problem \u0130kinci Node Geldi\u011finde Ba\u015flar"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">\u0130lk WebSocket implementasyonu genellikle olduk\u00e7a basittir.<br><br><code>Client \u2193 WebSocket Server<\/code><br><br>Client ba\u011flan\u0131r.<br>Connection bir <code>Map&lt;UserId, Socket&gt;<\/code> i\u00e7inde tutulur.<br>Mesaj geldi\u011finde socket&#8217;e g\u00f6nderilir.<br>Her \u015fey \u00e7al\u0131\u015f\u0131r.<br>Sonra ikinci server instance&#8217;\u0131n\u0131 a\u00e7ars\u0131n\u0131z.<br>Problemler orada ba\u015flar.<br>Kullan\u0131c\u0131 hangi node&#8217;da?<br>Architecture art\u0131k \u015f\u00f6yledir:<br><br><code>\u250c\u2500 WS Node A Client \u2192 LB \u2500\u253c\u2500 WS Node B \u2514\u2500 WS Node C<\/code><br><br>Ali Node A&#8217;ya ba\u011fl\u0131.<br>Ay\u015fe Node C&#8217;ye ba\u011fl\u0131.<br>Node A&#8217;n\u0131n memory&#8217;sindeki connection map Node C taraf\u0131ndan g\u00f6r\u00fclemez.<br>Ali Ay\u015fe&#8217;ye mesaj g\u00f6ndermek istedi\u011finde Node A&#8217;n\u0131n event&#8217;i Node C&#8217;ye ula\u015ft\u0131rmas\u0131 gerekir.<br>Bu nedenle connection state ile domain state&#8217;i birbirinden ay\u0131rmak \u00f6nemlidir.<br>Presence externalize edilmelidir<br>\u00d6rne\u011fin Redis:<br><br><code>presence:user:42 \u2192 node-c TTL: 30 seconds<\/code><br><br>gibi ephemeral connection metadata tutabilir.<br>Her node heartbeat ile presence bilgisini yeniler.<br>Node beklenmedik bi\u00e7imde kapan\u0131rsa TTL sonunda presence kayd\u0131 temizlenir.<br>Bu yakla\u015f\u0131m tek ba\u015f\u0131na b\u00fct\u00fcn problemi \u00e7\u00f6zmez ama node-local memory&#8217;ye olan ba\u011f\u0131ml\u0131l\u0131\u011f\u0131 azalt\u0131r.<br>Fan-out<br>Node A&#8217;dan Node C&#8217;ye event g\u00f6ndermek i\u00e7in Redis Pub\/Sub kullan\u0131labilir:<br><br><code>PUBLISH user:42 {...}<\/code><br><br>Node C ilgili kanal\u0131 dinler ve local socket&#8217;e mesaj\u0131 iletir.<br>Redis de Pub\/Sub&#8217;u realtime notifications, UI updates ve WebSocket node&#8217;lar\u0131 aras\u0131nda fan-out gibi kullan\u0131m alanlar\u0131 i\u00e7in konumland\u0131r\u0131yor.<br>Ancak \u00f6nemli bir detay var.<br>Redis Pub\/Sub <strong>at-most-once<\/strong> delivery sa\u011flar. Subscriber event g\u00f6nderildi\u011fi anda offline ise mesaj kaybolur. Redis&#8217;in kendi dok\u00fcmantasyonu persistent\/replay gereken durumlarda Streams gibi daha dayan\u0131kl\u0131 mekanizmalar\u0131n de\u011ferlendirilmesini \u00f6neriyor.<br>Dolay\u0131s\u0131yla:<br><br><code>typing indicator online presence live cursor<\/code><br><br>gibi ge\u00e7ici event&#8217;ler i\u00e7in Pub\/Sub gayet uygun olabilir.<br>Ama:<br><br><code>financial transaction match result order completed<\/code><br><br>gibi kaybolmamas\u0131 gereken olaylar\u0131n tek kayna\u011f\u0131 olmamal\u0131d\u0131r.<br>Reconnect normal davran\u0131\u015ft\u0131r<br>Mobil a\u011f Wi-Fi&#8217;dan 5G&#8217;ye ge\u00e7ti.<br>Telefon ekran\u0131 kapand\u0131.<br>Proxy connection&#8217;\u0131 sonland\u0131rd\u0131.<br>WebSocket disconnect ola\u011fan durumdur.<br>Client reconnect etti\u011finde:<br><br><code>lastReceivedSequence = 914<\/code><br><br>gibi bir cursor g\u00f6nderebilir.<br>Server ka\u00e7\u0131r\u0131lan durable event&#8217;leri tekrar sa\u011flayabilir.<br>Bunun i\u00e7in mesajlarda monoton sequence veya event ID kullanmak i\u015fleri kolayla\u015ft\u0131r\u0131r.<br>Backpressure<br>Realtime sistemlerde bir di\u011fer kritik problem \u00fcreticinin t\u00fcketiciden h\u0131zl\u0131 olmas\u0131d\u0131r.<br>Browser&#8217;\u0131n standart <code>WebSocket<\/code> API&#8217;sinin native backpressure mekanizmas\u0131 olmad\u0131\u011f\u0131n\u0131 MDN \u00f6zellikle belirtiyor; uygulama mesajlar\u0131 i\u015fleyemedi\u011finde buffering ve memory\/CPU problemleri olu\u015fabilir.<br>Bu nedenle outbound queue limitleri, rate limiting, message coalescing ve d\u00fc\u015f\u00fck \u00f6ncelikli event drop politikalar\u0131 gerekebilir.<br>WebSocket \u00f6l\u00e7eklendirmek \u201cRedis eklemek\u201d de\u011fildir.<br>Ger\u00e7ek problem connection ownership, transient versus durable event ayr\u0131m\u0131, reconnect semantics ve flow control tasarlamakt\u0131r.<br>Tek node&#8217;da \u00e7al\u0131\u015fan demo ile production realtime architecture aras\u0131ndaki fark tam olarak burada ortaya \u00e7\u0131kar.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0130lk WebSocket implementasyonu genellikle olduk\u00e7a basittir. Client \u2193 WebSocket Server Client ba\u011flan\u0131r.Connection bir Map&lt;UserId, Socket&gt; i\u00e7inde tutulur.Mesaj geldi\u011finde socket&#8217;e g\u00f6nderilir.Her \u015fey \u00e7al\u0131\u015f\u0131r.Sonra ikinci server instance&#8217;\u0131n\u0131 a\u00e7ars\u0131n\u0131z.Problemler orada ba\u015flar.Kullan\u0131c\u0131 hangi node&#8217;da?Architecture art\u0131k \u015f\u00f6yledir: \u250c\u2500 WS Node A Client \u2192 LB \u2500\u253c\u2500 WS Node B \u2514\u2500 WS Node C Ali Node A&#8217;ya ba\u011fl\u0131.Ay\u015fe Node C&#8217;ye ba\u011fl\u0131.Node A&#8217;n\u0131n [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[],"class_list":["post-27","post","type-post","status-publish","format-standard","hentry","category-mimari"],"_links":{"self":[{"href":"https:\/\/www.akifsen.com.tr\/blog\/wp-json\/wp\/v2\/posts\/27","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.akifsen.com.tr\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.akifsen.com.tr\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.akifsen.com.tr\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.akifsen.com.tr\/blog\/wp-json\/wp\/v2\/comments?post=27"}],"version-history":[{"count":1,"href":"https:\/\/www.akifsen.com.tr\/blog\/wp-json\/wp\/v2\/posts\/27\/revisions"}],"predecessor-version":[{"id":29,"href":"https:\/\/www.akifsen.com.tr\/blog\/wp-json\/wp\/v2\/posts\/27\/revisions\/29"}],"wp:attachment":[{"href":"https:\/\/www.akifsen.com.tr\/blog\/wp-json\/wp\/v2\/media?parent=27"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.akifsen.com.tr\/blog\/wp-json\/wp\/v2\/categories?post=27"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.akifsen.com.tr\/blog\/wp-json\/wp\/v2\/tags?post=27"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}