On bin istemcisi bağlı olan bir WebSocket sunucusunu yeniden başlatın, hoş olmayan bir şey olur. Her istemci bağlantının kesildiğini aşağı yukarı aynı anda fark eder, her istemci aynı sabit saniye sayısı kadar bekler ve her istemci aynı anda tekrar arar. Sunucu ayağa kalkar ve hemen on bin eşzamanlı TLS el sıkışmasıyla vurulur. Ya çöker ya da kısıtlama yapar, istemciler de yine aynı anda yeniden dener. Buna gürleyen sürü denir ve sabit bir yeniden bağlanma aralığı bunu garanti eder.
sgcWebSockets 2026.7, istemcilerin zaten zor durumdaki bir sunucuya yüklendiği iki yeri ele alıyor. WatchDog artık sabit bir aralık yerine üstel geri çekilme artı sarsıntı ile yeniden bağlanabiliyor ve HTTP istemcisi başarısız bir isteği kendi başına yeniden deneyebiliyor, sunucunun Retry-After ipucuna saygı göstererek ve aynı geri çekilme matematiğini kullanarak. Her ikisi de isteğe bağlıdır ve varsayılan olarak kapalıdır, dolayısıyla siz etkinleştirene kadar mevcut uygulamalar tam olarak eskisi gibi davranır.
Sabit yeniden bağlanma aralığının sorunu
WatchDog her zaman bir istemciyi bağlı tutmanın standart yolu olmuştur. Enabled ayarlarsınız, saniye cinsinden bir Interval ayarlarsınız ve bağlantı düştüğünde bileşen geri gelene kadar her Interval saniyede bir yeniden dener.
Bu, dengesiz bir Wi-Fi bağlantısındaki tek bir istemci için gayet iyidir. Bir filo için yanlış davranıştır. Sabit bir aralıkla filodaki her istemci aynı hızda tıklayan bir metronomdur ve bir sunucu yeniden başlatması hepsini senkronize eder: hepsi aynı anda bağlantısını kaybetti, dolayısıyla hepsi sonsuza kadar aynı anda yeniden bağlanır. Daha kötüsü, aralık uyum sağlamaz. Sunucu on dakika boyunca kapalıysa, Interval = 1 olan bir istemci altı yüz anlamsız bağlantı girişiminde bulunur ve herkes de aynısını yapar.
İstediğiniz şey bunun tam tersidir. İlk seferde çabuk yeniden deneyin, çünkü bağlantı kopmalarının çoğu geçicidir ve hızlı bir yeniden bağlanma kullanıcıya görünmez. Sonra geri çekilin, böylece gerçekten kapalı olan bir sunucuya yüklenilmez. Ve istemcileri yayın, böylece hepsi aynı anda gelmez.
WatchDog üzerinde üstel geri çekilme
WatchDog seçenek nesnesi dört özellik kazandı. Tür sgcTCP_Classes içinde yaşıyor:
type
TsgcWatchDogBackoff = (wdbFixed, wdbExponential);
Herhangi bir WebSocket bileşeninde seçeneklere Client.WatchDog ile ulaşılır ve geri çekilme ayarları şunlardır:
uses
sgcWebSocket, sgcTCP_Classes;
begin
sgcWebSocketClient1.WatchDog.Enabled := True;
sgcWebSocketClient1.WatchDog.Attempts := 0; // 0 = keep trying forever
sgcWebSocketClient1.WatchDog.Interval := 1; // seconds, the first delay
sgcWebSocketClient1.WatchDog.Backoff := wdbExponential;
sgcWebSocketClient1.WatchDog.BackoffMultiplier := 2.0; // double it every attempt
sgcWebSocketClient1.WatchDog.MaxInterval := 60; // seconds, the ceiling
sgcWebSocketClient1.WatchDog.Jitter := 0.2; // up to 20% random spread
end;
Matematik bilinçli olarak sıkıcıdır. İlk yeniden bağlanma girişimi Interval kadar bekler. Sonraki her girişim önceki gecikmeyi BackoffMultiplier ile çarpar ve sonuç MaxInterval değerine kırpılır. Yukarıdaki değerlerle gecikmeler 1sn, 2sn, 4sn, 8sn, 16sn, 32sn, 60sn, 60sn, 60sn şeklinde gider. İstemci yeniden bağlanır bağlanmaz sayaç sıfırlanır, dolayısıyla kısa bir ağ kesintisi size bir dakikaya değil bir saniyeye mal olur.
Interval ve MaxInterval her ikisi de, WatchDog'un her zaman olduğu gibi, saniye cinsinden ifade edilir. MaxInterval değerinin 0 olması tavan olmadığı anlamına gelir ve varsayılan budur. Üstel geri çekilmeyi etkinleştirirseniz bir tavan belirleyin, aksi hâlde gecikme saatlerle ölçülene kadar ikiye katlanmayı sürdürür.
Mevcut kullanıcılar için önemli ayrıntı: Backoff varsayılan olarak wdbFixed değerindedir. Yükseltir ve hiçbir şeyi değiştirmezseniz, istemciniz her zaman kullandığı aynı sabit aralıkla yeniden bağlanır. Gecikme hesaplaması yalnızca wdbExponential değerine geçtiğinizde veya bir Jitter ayarladığınızda kullanılır ve bu, HTTP yeniden denemesinin kullandığı yardımcı ile aynıdır, sgcBase_Helpers içindeki sgcNextBackoffMs.
Sarsıntı ve neden ölçekte önemli olduğu
Tek başına üstel geri çekilme sürüyü dağıtmaz. Yalnızca sürünün daha seyrek gelmesini sağlar. Hepsi 1sn, sonra 2sn, sonra 4sn bekleyen on bin istemci hâlâ birlikte gelen on bin istemcidir, sadece giderek büyüyen dalgalar hâlinde.
Onları gerçekten yayan şey Jitter özelliğidir. Hesaplanan gecikmenin bir kesridir, istemci başına ve girişim başına rastgele uygulanır. Jitter = 0.2 ile nominal 8 saniyelik bir gecikme, 8 saniye etrafında %20'lik bir bantta rastgele bir değere dönüşür, böylece istemci A 7,1sn'de, istemci B 8,6sn'de, istemci C 8,0sn'de uyanır. Filo bir sivri uç yerine yayılmış bir leke olarak gelir ve sunucunun kabul kuyruğu yetişebilir.
// A conservative fleet profile: fast first retry, hard ceiling, generous spread.
sgcWebSocketClient1.WatchDog.Enabled := True;
sgcWebSocketClient1.WatchDog.Interval := 2;
sgcWebSocketClient1.WatchDog.Backoff := wdbExponential;
sgcWebSocketClient1.WatchDog.BackoffMultiplier := 1.5; // gentler than doubling
sgcWebSocketClient1.WatchDog.MaxInterval := 120;
sgcWebSocketClient1.WatchDog.Jitter := 0.5; // spread over half the delay
Sarsıntı, geri çekilme kipinden bağımsızdır. Backoff özelliğini wdbFixed değerinde bırakıp yalnızca Jitter ayarlayabilirsiniz, bu da size istemcileri senkronizasyondan çıkarılmış klasik sabit aralığı verir. Bu çoğu zaman tek başına yeterlidir ve mevcut bir uygulamada yapılabilecek en küçük değişikliktir.
Bir HTTP isteğini otomatik olarak yeniden denemek
Özelliğin ikinci yarısı HTTP tarafındadır. Sunucu yeniden dağıtım yaptığı için 503 ile ya da hız sınırını aştığınız için 429 ile başarısız olan tek bir HTTP isteği aslında bir hata değildir. Birazdan tekrar gönderilmesi gereken bir istektir. O döngüyü elle yazmak sıkıcıdır ve herkes onu biraz yanlış yazar.
TsgcIdHTTP artık, sgcHTTP_Client içinde TsgcIdHTTPRetry_Options olarak bildirilen bir RetryOptions nesnesine sahip. Etkinleştirin, istek kendini yeniden dener:
uses
sgcHTTP_Client;
var
oHTTP: TsgcIdHTTP;
begin
oHTTP := TsgcIdHTTP.Create(nil);
try
oHTTP.RetryOptions.Enabled := True; // default False
oHTTP.RetryOptions.MaxRetries := 3;
oHTTP.RetryOptions.InitialInterval := 500; // milliseconds
oHTTP.RetryOptions.MaxInterval := 30000; // milliseconds
oHTTP.RetryOptions.Multiplier := 2.0;
oHTTP.RetryOptions.Jitter := 0.2;
oHTTP.RetryOptions.StatusCodes := '429,502,503,504';
ShowMessage(oHTTP.Get('https://api.example.com/v1/orders'));
finally
oHTTP.Free;
end;
end;
Buradaki aralıklar saniye değil milisaniye cinsindendir, çünkü HTTP yeniden denemeleri bir yeniden bağlanmadan çok daha kısa bir zaman ölçeğinde yaşar. Gecikmeler WatchDog ile aynı eğriyi izler: 500ms, 1sn, 2sn, 4sn, MaxInterval ile sınırlanmış ve Jitter ile yayılmış.
StatusCodes, yeniden denenebilir sayılan HTTP durum kodlarının virgülle ayrılmış bir dizge hâlindeki listesidir. Varsayılan 429,502,503,504 değeridir, bunlar "isteğiniz yanlış" değil "şimdi olmaz, tekrar deneyin" anlamına gelen kodlardır. Bir 400 veya bir 401 asla yeniden denenmez, çünkü aynı bozuk isteği dört kez daha göndermek kimseye yardımcı olmaz. Reddedilen bir soket veya bir okuma zaman aşımı gibi bağlantı seviyesindeki hatalar da yeniden denenir.
Aynı nesne, kütüphanedeki hazır her REST istemcisinin temel sınıfı olan sgcHTTP_API içindeki TsgcHTTPAPI_client üzerinde de bulunur. Böylece yeniden deneme ilkesi, istemci başına hiçbir tesisat gerektirmeden hepsinde kullanılabilir:
// Any API client that descends from TsgcHTTPAPI_client
sgcHTTPAPI_Client1.RetryOptions.Enabled := True;
sgcHTTPAPI_Client1.RetryOptions.MaxRetries := 5;
sgcHTTPAPI_Client1.RetryOptions.StatusCodes := '429,500,502,503,504';
Retry-After: hızı sunucu belirlesin
Kendi geri çekilmenizi hesaplamak bir tahmindir. Bir sunucu 429 veya 503 döndürdüğünde, Retry-After yanıt başlığında, ya saniye sayısı ya da bir HTTP tarihi olarak, ne kadar bekleyeceğinizi genellikle tam olarak söyler. O sayı bir öneri değildir. Sunucunun sizi ne zaman tekrar kabul edeceğini söylemesidir ve bunu yok sayıp kendi programına göre yeniden deneyen bir istemci basitçe tekrar reddedilecektir.
HonorRetryAfter varsayılan olarak True değerindedir. Yanıt bir Retry-After başlığı taşıdığında, o değer hesaplanan geri çekilmeye üstün gelir ve istemci sunucunun istediği kadar bekler:
oHTTP.RetryOptions.Enabled := True;
oHTTP.RetryOptions.HonorRetryAfter := True; // default True
oHTTP.RetryOptions.InitialInterval := 500;
oHTTP.RetryOptions.MaxRetries := 3;
// Server replies 429 with "Retry-After: 12".
// The computed backoff would have been 500ms, the client waits 12 seconds instead.
Başlığın her iki biçimi de ayrıştırılır, delta saniye biçimi ve HTTP tarih biçimi. Bu, OpenAI ve Anthropic'in 429 döndürdüklerinde istemcilerinden yapmalarını istedikleri şeyin tam olarak kendisidir ve bir hız sınırından toparlanan bir istemci ile hız sınırlı kalmaya devam eden bir istemci arasındaki farktır.
AI istemcilerinde yeniden denemeler
AI istemcileri, yeniden denemelerin hakkını verdiği yerdir, çünkü oradaki hız sınırları istisnai değil rutindir. Her biri kendi yeniden deneme seçenekleri nesnesini kendi *Options özelliği üzerinde açığa çıkarır, dolayısıyla ayarlar Nesne Denetçisi'nde model ve API anahtarının yanında görünür.
TsgcHTTP_API_OpenAI üzerinde yol OpenAIOptions.RetryOptions şeklindedir, sınıfı TsgcHTTPOpenAIRetry_Options:
uses
sgcHTTP_API_OpenAI;
begin
sgcHTTPOpenAI1.OpenAIOptions.RetryOptions.Enabled := True;
sgcHTTPOpenAI1.OpenAIOptions.RetryOptions.Retries := 3;
sgcHTTPOpenAI1.OpenAIOptions.RetryOptions.Wait := 10000; // ms, first delay
sgcHTTPOpenAI1.OpenAIOptions.RetryOptions.MaxInterval := 30000; // ms, ceiling
sgcHTTPOpenAI1.OpenAIOptions.RetryOptions.Multiplier := 2.0;
sgcHTTPOpenAI1.OpenAIOptions.RetryOptions.Jitter := 0.2;
sgcHTTPOpenAI1.OpenAIOptions.RetryOptions.HonorRetryAfter := True;
end;
Anthropic aynı yapıya AnthropicOptions.RetryOptions altında, Gemini ise GeminiOptions.RetryOptions altında sahiptir. Aynı desen Grok, Mistral, DeepSeek ve Ollama istemcilerinde, ayrıca yeniden denenen bir ödeme çağrısının çok farklı türde bir önem taşıdığı Stripe ve Paddle istemcilerinde de vardır.
// Anthropic returns 429 and 529 under load, both are treated as retryable.
sgcHTTPAnthropic1.AnthropicOptions.RetryOptions.Enabled := True;
sgcHTTPAnthropic1.AnthropicOptions.RetryOptions.Retries := 3;
sgcHTTPAnthropic1.AnthropicOptions.RetryOptions.HonorRetryAfter := True;
AI nesnelerindeki özellik adlarının, o bileşenlerde başka yerlerde zaten kullanılan adlandırmayla tutarlı kalmak için, MaxRetries ve InitialInterval yerine Retries ve Wait olduğuna dikkat edin. Davranış aynıdır ve içeride, alttaki HTTP istemcisinde aynı TsgcIdHTTPRetry_Options nesnesini yapılandırırlar.
Neden başarısız olduğunu bilmek: Connect ve LastError
Bir yeniden deneme döngüsü ancak hata işlemesi kadar iyidir. Bir hatanın yeniden denemeye değip değmediğine karar verebilmeniz için hatanın ne olduğunu bilmeniz gerekir, ve şimdiye kadar bir WebSocket istemcisinde bunu öğrenmenin tek yolu bir OnError işleyicisi bağlamak ve mesajı bir yerdeki alana saklamaktı.
2026.7, sgcWebSocket_Client_Base içinde hatayı size doğrudan veren bir Connect aşırı yüklemesi ekliyor:
function Connect(const aTimeout: Integer = 10000): Boolean; overload;
function Connect(out AError: string; const aTimeout: Integer = 10000): Boolean; overload;
Böylece elle yazılmış bir yeniden bağlanma döngüsü nedeni inceleyip kendi kararını verebilir:
var
vError: string;
begin
if not sgcWebSocketClient1.Connect(vError, 5000) then
begin
// A DNS failure or a refused socket is worth retrying.
// A rejected certificate or a 401 on the handshake is not.
Memo1.Lines.Add('connect failed: ' + vError);
Exit;
end;
end;
Son hata bileşenin kendisinde de tutulur. sgcWebSocket_Classes içindeki TsgcWSComponent_Base, salt okunur bir LastError: string özelliği ile bir ClearLastError yöntemi yayımlar, böylece nedeni sonradan okuyabilir ve ölçmek istediğiniz bir girişimden önce sıfırlayabilirsiniz:
sgcWebSocketClient1.ClearLastError;
if not sgcWebSocketClient1.Connect(5000) then
ShowMessage('connect failed: ' + sgcWebSocketClient1.LastError);
Bunların hiçbiri, her zaman olduğu gibi tetiklenmeye devam eden OnError olayının yerini almaz. Sadece şu anlama gelir: basit, eşzamanlı bir kod parçasının, basit bir soruyu yanıtlamak için artık bir olay işleyicisi kurması gerekmez.
Kullanılabilirlik
WatchDog geri çekilmesi, sarsıntı, HTTP yeniden denemeleri ve yeni Connect aşırı yüklemesi, sgcWebSockets 2026.7'den itibaren Delphi 7'den 13'e kadar ve C++Builder'da, Win32/Win64, Linux64, macOS, Android ve iOS platformlarında kullanılabilir. Burada anlatılan her şey varsayılan olarak kapalıdır. Backoff değeri wdbFixed, Jitter değeri 0 ve RetryOptions.Enabled değeri False olduğundan, mevcut bir uygulama hiçbir davranış değişikliği olmadan yükseltilir ve siz mantıklı olduğu yerde devreye alırsınız.
Etkin aboneliği olan müşteriler yeni derlemeyi müşteri alanından indirebilir. Deneme kullanıcıları güncellenmiş kurulum dosyasını esegece.com/products/websockets/download adresinden alabilir.
Sorularınız, geri bildirimleriniz veya filonuz için bir geri çekilme profili seçmek konusunda yardıma mı ihtiyacınız var? Bize ulaşın, kodu yazan kişilerden yanıt alacaksınız.
