Geri Basınç: Yavaş WebSocket İstemcileri için Gönderim Kuyruğu Sınırları | eSeGeCe Blog

Geri Basınç: Yavaş WebSocket İstemcileri için Gönderim Kuyruğu Sınırları

· Bileşenler

Hızlı bir akışı yayınlayan bir sunucu, mesajları en yavaş istemcinin hızında değil, kendi hızında üretir. Piyasa verisi, telemetri, canlı bir günlük akışı: sunucu iter ve bağlı her istemcinin buna ayak uydurması beklenir. Çoğu uydurur. İçlerinden biri, er ya da geç, uyduramaz.

Bir istemci soketini boşaltmayı bıraktığında, ona gönderdiğiniz mesajlar yok olmaz. O bağlantının gönderim kuyruğunda birikirler ve kuyruk bellek demektir. Tren tüneline giren bir telefon, kısıtlanan bir tarayıcı sekmesi, okuma çağrısını yapmayı bırakan bir istemci süreci, hepsi sessizce sunucunuzun belleğini bir şey çökene kadar büyütür. sgcWebSockets 2026.7, bu kuyruğu sınırlamanıza ve dolduğunda ne olacağına karar vermenize olanak tanır.

Yavaş tüketici

Sorun ağın yavaş olması değil, uyumsuzluktur. Akışınız saniyede 500 mesaj yayımlar. Kötü bir hat üzerindeki bir istemci 50 tanesini boşaltır. Kalan 450'sinin bir yere gitmesi gerekir ve o "bir yer", sunucu sürecindeki bağlantı başına bir listedir. Bir dakika sonra o tek bağlantı on binlerce mesaj tutuyordur. On dakika sonra RAM'inizi tutuyordur.

Zarar yavaş istemciyle de sınırlı değildir. Onun yediği bellek, sağlıklı istemcilerin alamadığı bellektir ve süreç takas alanına inmeye ya da bellek ayırıcı zorlanmaya başladığında herkesin gecikmesi de onunla birlikte gider. Tünelde kalan bir telefon, herkes için bir kesintiye dönüşür.

Şimdiye kadar sgcWebSockets içindeki gönderim kuyruğu sınırsızdı; bu, her istemci ayak uydurduğu sürece sorunsuz, biri uyduramadığı ilk anda ise tatsız bir durumdur. Çözüm, ona bir tavan koymaktır.

Kuyruğu sınırlamak

Kuyruk, bileşenin QueueOptions özelliği altında, mevcut Text, Binary ve Ping seviye alt seçeneklerinin yanında yer alır. İki yeni özellik onu sınırlar: MaxQueueSize, tek bir bağlantının kaç bekleyen mesaj tutabileceğine tavan koyar ve OverflowPolicy, bu tavana ulaşıldığında bir sonraki mesajla ne yapılacağını söyler.

uses
  sgcWebSocket, sgcWebSocket_Classes, sgcWebSocket_Types;

begin
  // messages are queued per connection and flushed by the queue thread
  oServer.QueueOptions.Text.Level   := qmLevel1;
  oServer.QueueOptions.Binary.Level := qmLevel1;

  // at most 1000 pending messages per connection
  oServer.QueueOptions.MaxQueueSize := 1000;

  // when the queue is full, throw away the oldest pending message
  oServer.QueueOptions.OverflowPolicy := qopDropOldest;
end;

QueueOptions, TsgcWebSocketServer, TsgcWebSocketHTTPServer ve TsgcWebSocketClient bileşenlerinde published'dır ve bileşen, kabul edildiği anda onu her bağlantıya kopyalar; dolayısıyla sınır sunucu başına değil, bağlantı başınadır. MaxQueueSize = 1000 ile bin bağlantı, her birinde bin bekleyen mesajlık bir en kötü durum verir ve bu en kötü durum artık dağıtımdan önce kâğıt üzerinde çarpabileceğiniz bir sayıdır.

Bir taşma politikası seçmek

Burada evrensel olarak doğru bir yanıt yoktur, üç tane olmasının nedeni de budur. İstediğiniz politika, mesajlarınızın ne anlama geldiğine bağlıdır.

qopDropOldest, yalnızca en yeni değerin önemli olduğu canlı bir akış için doğrudur. Bir fiyat kotasyonu, bir sensör okuması, bir gösterge paneli ölçeri: dört saniye önceki mesaj, bir sonraki geldiği anda değersizdir. Kuyruğun önünden atmak, geride kalıp sonra toparlanan istemcinin, yetişmeden önce çiğnemek zorunda olduğu bayat bir birikim yerine güncel resmi almasını sağlar. Bu, varsayılandır.

qopDropNewest, önemli olanın önceki mesajlar olduğu ve yeni yükü atmayı tercih ettiğiniz durumlar için doğrudur. Bir istemci bir dizi boyunca yürütülüyorsa ve o dizinin başı anlamlı kısımsa, zaten kuyrukta olanı koruyun ve yeni geleni reddedin.

qopDisconnect, ayak uyduramayan bir istemcinin istemediğiniz bir istemci olduğu durumlar için doğrudur. Dürüst seçenek budur: o bağlantıyı sessizce, boşluklarla dolu ve bozuk bir akış görüntüsüyle beslemek yerine onu kapatırsınız. İstemcinin WatchDog bileşeni yeniden bağlanır, istemci yeniden abone olur ve bilinen bir durumdan yeniden başlar. Bağlantısının kesildiğini bilen bir istemci toparlanabilir. Mesaj kaybettiğini bilmeyen bir istemci toparlanamaz.

// A market data feed: only the newest tick is worth anything.
oServer.QueueOptions.MaxQueueSize   := 500;
oServer.QueueOptions.OverflowPolicy := qopDropOldest;

// A sequence where the head matters: shed the new load instead.
oServer.QueueOptions.OverflowPolicy := qopDropNewest;

// A client that falls this far behind is a client we do not want.
oServer.QueueOptions.OverflowPolicy := qopDisconnect;

Hangisini seçerseniz seçin, bilinçli seçin. Mesaj atmak bir ödünleşmedir ve bu üç değerin amacı, hangi ödünleşmeyi yaptığınızı üretimde keşfetmek yerine kendiniz seçebilmenizdir.

Atmadan önce bilmek: OnSendBufferFull

Sessizce mesaj atan bir kuyruk, size yalan söyleyen bir kuyruktur. OnSendBufferFull bunun çıkış kapısıdır. Herhangi bir şey atılmadan önce tetiklenir, size bağlantıyı ve o anki kuyruk boyutunu verir ve var DropMessage parametresi kodunuzun atmayı veto etmesine olanak tanır.

procedure TForm1.WSServerSendBufferFull(Connection: TsgcWSConnection;
  const QueueSize: Integer; var DropMessage: Boolean);
begin
  // fires BEFORE anything is dropped: log it, count it, alert on it
  DoLog('slow consumer ' + Connection.IP + ' [' + Connection.Guid + '] queue=' +
    IntToStr(QueueSize));
  Inc(FSlowConsumerHits);

  // this connection is a paying feed we must not truncate: keep the message
  // and let the queue grow past the cap for this one send
  if IsPriorityConnection(Connection) then
    DropMessage := False;
end;

DropMessage := False ayarlarsanız, o mesaj için taşma politikası atlanır: mesaj yine de, tavanın üzerinde olsa bile kuyruğa alınır. Bunu idareli kullanın, çünkü her zaman uyguladığınız bir veto, hiç sınır olmamasıyla aynı şeydir. Olayın gündelik kullanımı sıkıcı olanıdır ve değerli olanı da odur: bunun olduğunu, hangi bağlantının başına geldiğini ve o bağlantının ne kadar geride kaldığını bilmek. O sayı, süreç belleği tüketmeden çok önce elinizdeki erken uyarıdır.

OnSendBufferFull, TsgcWebSocketClient, TsgcWebSocketServer ve TsgcWebSocketHTTPServer bileşenlerinde published'dır.

Hiçbir şeyi atmayı göze alamadığınızda ne yapmalı

Bazen dürüst yanıt, hiçbir mesajın kaybolmaması gerektiğidir. Durumunuz buysa, yukarıdaki üç politikanın hiçbiri istediğiniz şey değildir, daha büyük bir kuyruk da değildir. Daha büyük bir kuyruk sorunu çözmez, yalnızca erteler ve onu daha kötü bir ana erteler: bir yoğunluk patlamasının daha derinine, bellek baskısının daha içine ve hâlâ bir şeyler yapabileceğiniz noktadan daha uzağa.

Yanıt, farklı bir tasarımdır. Teslim edilmemiş mesajları bir soket kuyruğunda tutmayın, onları kalıcı hâle getirin ve istemcinin kaçırdıklarını, elverdiği zaman almasına izin verin. mesaj geçmişi ve yeniden bağlanma kurtarması tam olarak bunu yapar: sunucu, offset değerleriyle kanal başına sınırlı bir geçmiş tutar ve geride kalan ya da bağlantısı kopan bir istemci, yeniden abone olduğunda kaçırdığı mesajları yeniden oynatır. Bunu qopDisconnect ile eşleştirin ve mevcut en güçlü bileşimi elde edin: ayak uyduramayan bir istemci hızla koparılır ve geri döndüğünde, kaçırmadığı gibi davranmak yerine kaçırdığı her şeyi kurtarır.

Varsayılan hâlâ sınırsız

MaxQueueSize varsayılan olarak 0 değerindedir, bu da sınırsız anlamına gelir; yani tam olarak sgcWebSockets'in eskiden gösterdiği davranıştır. Sürüm yükseltmek hiçbir şeyi değiştirmez. Pozitif bir tavan ayarlamadıkça taşma yoluna girilmez bile, dolayısıyla mevcut sunucular siz aksine karar verene kadar her zamanki gibi kuyruğa almaya devam eder.

Bununla birlikte, denetlemediğiniz istemcilere yayın yapıyorsanız, ki genel internette onları asla denetleyemezsiniz, bağlantı başına sınırsız bir kuyruk, size bağlanacak en kötü istemci adına verilmiş açık uçlu bir bellek taahhüdüdür. Bir tavan koymanın maliyeti tek bir satırdır.

Kullanılabilirlik

Gönderim kuyruğu sınırları, üç taşma politikası ve OnSendBufferFull, sgcWebSockets 2026.7 sürümünde Delphi 7'den 13'e kadar olan sürümlerde ve C++Builder'da, Win32/Win64, Linux64, macOS, Android ve iOS üzerinde sunulur.

Etkin aboneliği olan müşteriler yeni derlemeyi müşteri alanından indirebilir. Deneme kullanıcıları güncellenmiş yükleyiciyi esegece.com/products/websockets/download adresinden edinebilir.

Sorularınız, geri bildirimleriniz veya akışınız için doğru politikayı seçme konusunda yardıma mı ihtiyacınız var? Bize ulaşın, yanıtı kodu yazan kişilerden alacaksınız.