Net-Base Žurnalas

08.08.2026

REST klientas Delphi: atsparus laiko išeigoms (timeouts), pakartotiniams bandymams (retries) ir 429 rate-limitams su backoff

Jei REST-užklausos Delphi sporadiškai užstringa, sukelia laiko limitų klaidas (timeouts) arba grįžta su 429 rate-limitais, „tiesiog dar kartą išsiųsti“ nepakanka. Šis praktinis straipsnis parodo, kaip su RESTClient nustatyti kontroliuojamus laiko limitus (timeouts), saugius pakartotinius bandymus (retries), backoff su jitter ir tvarkingą žurnavimą...

08.08.2026

Nuo žurnalo temos iki projekto įgyvendinimo

Tinkami puslapiai apie paslaugas ir techninę informaciją šiam įrašui

Vienas REST kvietimas teoriškai yra paprastas: užklausa išsiųsta, atsakymas gautas, ir viskas. Praktikoje produktyvios integracijos retai žlunga dėl „neteisingo URL“ — dažniau problemos kyla eksploatacijos kraštutinėse situacijose: periodiški laiko išsipildymai, trumpalaikės DNS arba TLS problemos, perkrauti downstream sistemos arba 429 (Too Many Requests), kai API tarpininkas drosselina. Būtent čia demonstracinis prototipas atsiskiria nuo ilgalaikės, prižiūrimos integracijos.

Šiame įraše parodyta, kaip su RESTClient in Delphi sukurti atsparius komunikacijos kanalus: aiškiai apibrėžti timeout’ai, tikslingi retry tik ten, kur tai techniškai ir funkciškai saugu, bei backoff elgsena, kuri gerbia rate‑limit’us vietoje jų eskalavimo. Dėmesys nėra „gražių“ kodo fragmentų rašyme, o elgesyje esant apkrovai, derinimo (debugging) gebose, tvarkingo klaidų klasifikavimo ir klausime, kada papildomas darbas iš tiesų atsiperka.

Kodėl timeout’ai, retry ir 429 realiame aplinkoje pasirodo kartu

Įmonių tinkluose REST kvietimai retai vyksta „tiesiai į internetą“. Tipiški elementai yra proxy grandinės, TLS terminavimas, API gateway’ai, WAFs (Web Application Firewall) ir keli vidiniai hop’ai. Kiekvienas mazgas gali turėti savus timeout’us ir limitus. Client pusės timeout’as gali reikšti:

  • Serveris neatsakė (perkrova, deadlock, downstream stringa).
  • Atsakymas atėjo, bet per vėlai (blogas maršrutas, paketų praradimas, congestion).
  • Tu pats save užblokavai: per trumpi timeout’ai arba užblokuojantis UI-/pagrindinis thread’as.

Paraleliai „naivūs“ retry dažnai sukuria papildomų problemų: jei serveris jau veikia maksimaliu pajėgumu, retry didina apkrovą ir mažą resursų trūkumą paverčia incidente. 429 atveju tai ypač aišku: rate‑limit yra tiesioginė užklausa siųsti mažiau arba sugrįžti vėliau. Klientas be backoff elgiasi kaip neintencionalus DoS generatorius.

Atsparumas todėl nekyla iš „retry visur“, o iš nuoseklaus sprendimų modelio: kurios klaidos yra tranzitinės (laikinės), kurios — nuolatinės, kurie request’ai yra retry’intini (idempotentiški), ir kaip valdyti laukimo intervalus, kad sistema išliktų stabili.

Timeout’ų teisingas nustatymas: ką konkrečiai reiškia „Timeout“ RESTClient’e in Delphi?

Dažnas klupimo taškas: „Timeout“ nėra vienas ir tas pats timeout. Priklausomai nuo stack’o yra skirtingos fazės. Net jei Delphi‑REST komponentai daug ką kapsuliuoja, verta turėti modelį galvoje:

  • Connect‑Timeout: laikas, per kurį užsikuria TCP jungtis (įskaitant DNS/TLS, priklausomai nuo implementacijos).
  • Read/Response‑Timeout: laikas, per kurį serveris turi pradėti siųsti baitus arba užbaigti atsakymą.
  • Bendras timeout: viršutinė riba visam kvietimui, įskaitant retry ciklus.

Praktikoje per trumpas timeout’as yra bent jau tiek pat pavojingas, kiek ir per ilgas: sukuriate dirbtines klaidas, kurios vėliau retry’inamos ir taip generuoja papildomą apkrovą. Priešingai, per ilgas timeout’as blokuoja worker thread’us, eilių (queue) vietas arba UI reagavimo laiką. Eksploatacijai ir administravimui svarbu, kad timeout’ai būtų konfigūruojami (pvz. pagal endpoint) ir kad jų reikšmės būtų fiksuojamos žurnale (log).

Praktinė rekomendacija: dvi lygmenys vietoje vieno skaičiaus

REST kvietimams verslo programinėje įrangoje pasiteisino du lygmenys:

  • Call‑Timeout (vienam Request): realistiška viršutinė riba, atitinkanti konkretų use case.
  • Užduoties laiko limitas (bendras): jei turite partijinį apdorojimą arba sinchronizacijos darbą, apribokite bendrą vykdymo laiką ir tvarkingai nutraukite.

Taip išvengsite, kad atskiras API atsakymas lauktų amžinai, ir tuo pačiu, kad naktinis darbas dėl daugelio pakartojimų truktų „iki pietų“.

Pakartojimų sprendimas: ne techninis, o domeninis

Ar pakartojimas leidžiamas — tai ne vien techninis klausimas. Pagrindinis terminas yra idempotencija: užklausa yra idempotentė, jei ją vykdant kelis kartus gaunamas toks pat efektas, kaip ir vieną kartą. Tipiški pavyzdžiai: GET yra idempotentas, PUT dažnai taip pat (kai tikslinis objektas nustatomas pilnai), DELETE paprastai taip pat. POST dažnai nėra idempotentas (pvz. „sukurti naują užsakymą“).

Kodėl tai svarbu? Timeout gali reikšti, kad serveris užklausą vis dėlto apdorojo, tačiau atsakymas nepasiekė kliento. Jei tada aklai pakartosite POST, sukursite dublikatus. Tai operacijoje klasikinė „vaiduoklių klaida“: aplikacijoje rodoma „Timeout“, o backend’e yra dvigubi įrašai.

Saugus pagrindas: pakartojimus leisti tik aiškiai pakartojamoms operacijoms

Tvirta taisyklė, patvirtinta integracijose:

  • GET: pakartojamas tranzitorinių klaidų atveju.
  • PUT/DELETE: pakartojami, jei jūsų API tai aiškiai domeniškai apibrėžia (pvz. resurso ID yra stabili) ir serveris teisingai įgyvendina idempotenciją.
  • POST: tik pakartojamas, jei turite idempotentumo rakto strategiją (domeniškai vienareikšmė užklausos ID, kuri serverio pusėje neleidžia dublikatų) arba jei POST yra semantiškai idempotentas (retai, bet įmanoma).

Jei nekontroliuojate API, tai yra ta vieta, kur kaip techninis vadovas turite priimti sprendimą: arba priimate „nėra pakartojimų POST atveju“ (ir kuriate geresnes klaidų žinutes / resinkronizavimo mechanizmus), arba deratės su API tiekėju dėl idempotentumo rakto arba dėl deduplikavimo modelio.

429 Too Many Requests: gerbkite užklausų apribojimus vietoje „aklinio pakartojimo“

Grafik eines API-Gateways mit gedrosselten Requests und Backoff-Abständen
Esant 429 padeda kontroliuojamas backoff: mažiau vienu metu vykdomų pakartojimų, stabilesnis atsigavimas.

HTTP 429 nėra „trukdanti klaida“, o valdymo mechanizmas. Verslo aplinkose 429 dažnai kyla dėl:

  • API-Gateway su Token-Bucket/Leaky-Bucket apribojimais (užklausų dažnio ribojimas).
  • Debesų API su nuomininkų limitais per minutę/valandą.
  • Vidiniai servisai, kurie saugosi nuo apkrovos pikų.

Klientui tai reiškia: pakartojimai – taip, bet kontroliuojami. Svarbu du dalykai:

  • Įvertinti Retry-After antraštę, jei ji yra (sekundėmis arba HTTP data formatu).
  • Naudoti backoff, kai nėra Retry-After arba jei papildomai naudojate jitter.

Pats dažniausias spąstas: 429 tvarkomas kaip 500 („serverio klaida, pakartokite iš karto“). Tokiu būdu stiprinate ribojimą. Geriau: 429 yra signalas, aktyviai palaukti ir, prireikus, sumažinti paralelizmą.

Backoff su Jitter: kodėl be atsitiktinumo viskas sinchroniškai žlunga

Exponential Backoff reiškia, kad po kiekvieno klaidingo bandymo didini laukimo laiką (pvz. 200 ms, 400 ms, 800 ms …). Jitter yra atsitiktinumo dalis, kuri neleidžia daugeliui klientų vėl užklausti tuo pačiu metu. Be Jitter praktikoje dažnai nutinka štai kas: įsijungia limitas, 50 klientų gauna 429, visi tiksliai laukia 1 sekundę ir tada vėl siunčia vienu metu. Rezultatas: vėl 429 ir gauni „Thundering Herd“ problemą.

Praxėje tinkamas požiūris yra „Full Jitter“ arba „Equal Jitter“: apskaičiuoji Backoff langą ir tuomet parenki atsitiktinį laukimo laiką šiame lange. Atrodo kaip detalė, bet eksploatacijoje tai lemia skirtumą tarp stabilaus atsigavimo ir nuolatinių pasikartojančių klaidų.

Ein sauberes Muster: REST-Aufrufe kapseln, statt überall Retry-Schleifen zu verstreuen

Jei Retries/Backoff įdiegi „ad hoc“ kiekvienoje iškvietimo vietoje, greitai susidaro nekonsistentiškas elgesys: vienas Endpoint agresyviai kartoja bandymus, kitas išvis ne, Logging būna fragmentiškas, o administratoriai mato tik „sporadines klaidas“. Robustus sprendimas atsiranda, kai apibrėži centralizuotą kvietimų kelią:

  • Wrapperis aplink RESTClient/RESTRequest, kuris taiko Policy (Timeout, Retry, Backoff).
  • Vieningas rezultato objektas: būsenos kodas, trukmė, bandymų skaitiklis, prireikus paskutinė išimtis.
  • Standartizuotas Logging (Request-ID/Correlation-ID, Endpoint, HTTP metodas, atitinkamos antraštės).

Čia yra ta vieta, kur papildomas kodas iš tiesų atsiperka: gauni reprodukuojamą elgesį, geresnius logus ir gali konfigūruoti Policies pagal tikslinę sistemą, nekeisdamas programos struktūros.

Policy-Entscheidungsmatrix (kurz und praktisch)

Daugumai integracijų užtenka paprastos matricos, kurią įgyvendini wrapperyje:

  • Retry bei: tinklo klaidos/ryšio nutrūkimai, 408, 429, 502, 503, 504 (priklausomai nuo API susitarimo).
  • Kein Retry bei: 400/401/403/404 (dažniausiai konfigūracijos/autentifikacijos/užklausos klaidos), 409/422 (verslo konfliktai/validacija), taip pat POST be Idempotency-Key.
  • Max. Versuche: išlaikyti mažą (dažnai pakanka 2–4 bandymų), už tai geresnis monitoringas.
  • Max. Backoff: riboti (pvz. kelios sekundės iki vienos minutės), kitu atveju užblokuosi per daug worker73.

Svarbu: šios taisyklės nėra universalios. 404 prie „eventual consistency“ gali būti laikina, 409 prie užrakinimo strategijų taip pat gali būti tranzitorinė. Skirtumas yra tas, kad tai turi būti sąmoningas nukrypimas, o ne atsitiktinis elgesys.

Konkreter Randfall: Timeout nach POST 6 war es jetzt gespeichert oder nicht?

Popierinės schemos ir užrašai, vaizduojantys neaiškų POST būseną po timeout'o
Timeout po POST yra pavojingas: be idempotencijos būsenos lieka fachliškai neaiški.

Tai klasika, kurią retai pavyksta švariai atkurti debugeryje: siunčiate POST (pvz. „Sukurti bilietą“), jūsų klientas gauna Read-Timeout, o vartotojas paspaudžia „dar kartą“. Tačiau backend’e bilietas jau egzistuoja. Be atsakomųjų priemonių susidaro dublikatai arba nekonsistencijos.

Tik trimis strategijomis tai tampa patikima:

  • Idempotency-Key: sugeneruojate kiekvienam funkciniam veiksmui unikalų užklausos ID (pvz., GUID), siunčiate jį kaip antraštę, o serveris garantuoja dublikatų eliminavimą apdorojimo metu.
  • Kliento pusės deduplikacija: vietoje išsaugote laukiančias užklausas su savo ID ir po timeout atliekate būsenos patikrinimą (pvz., GET pagal funkcininį raktą). Tai sudėtingiau ir ne visada įmanoma.
  • Be pakartojimo: aiškiai pranešate, kad būsena nežinoma, ir sukuriate rankinį arba automatizuotą resync‑procesą (pvz., vėlesnį suderinimą).

Jei kuriate integracijas eksploatavimui, „Status unbekannt“ yra galiojanti kategorija. Nesistenkite neapibrėžtumo užkoduoti. Registruokite jį, padarykite matomą ir numatykite suderinimo kelią.

Backoff‑dizainas praktikoje: ribos, lygiagretumas ir atšaukimas

Backoff nėra tik „Sleep“. Jį reikia įdėti į jūsų programos kontekstą:

  • Lygiagretumas: jei turite 20 gijų ir visos laukia, 20 gijų bus užblokuota. Tai dažnai priimtina servisams, bet ne darbalaukio programoms.
  • Atšaukimas: vartotojas nutraukia, servisas sustoja, darbas baigiamas. Backoff laukimas turi būti atšaukiamas, kitaip sustabdymo/išjungimo procesai užstrigs.
  • Sąžiningumas: keli galiniai taškai neturėtų viena kitą išalkinti. Rate‑Limits dažnai taikomi pagal tokeną arba pagal endpointą; jūsų apvalkalas turėtų sugebėti reguliuoti pagal tikslinę sistemą.

Švari praktika — įdiegti backoff funkciją, kuri laukia mažais intervalais ir tikrina atšaukimo žymę (pvz., Event/Token). Tai nėra prabanga: būtent ši vieta nulemia, ar Windows- ir Linux-servisai tvarkingai sustos arba Service Control Manager konsolėje „užstrigs“.

Maksimali trukmė ir „biudžetas“ vienai užklausai

Patikima retry‑implementacija dirba ne tik su „max tries“, bet ir su laiko biudžetu. Pavyzdys: leidžiate maksimalią 10 sekundžių bendrą trukmę užklausai įskaitant pakartojimus. Tokiu atveju vienas bandymas negali staiga užblokuoti 30 sekundžių vien todėl, kad timeout klaidingai nustatytas. Adminams ir eksploatavimui tai ypač vertinga, nes riboja latenčijos piko reiškinius ir stabilizuoja eiles.

Derinimas ir eksploatacijos diagnostika: be gerų žurnalų pakartojimai yra nematomi klaidų stiprintuvai

Darbo vietos scena su neryškiais žurnalais ir eskizuotu kontekstu dėl Request‑ID ir pakartojimų
Su Correlation‑ID, bandymų skaitikliu ir trukme pakartojimai eksploatacijoje tampa atsekami.

Pakartotiniai bandymai be žurnavimo yra pavojingi, nes galiausiai išgirsti tik „kartais užtrunka“. Jei nori tapti robustesnis, tau reikia žurnalų, kurie ne tik išveda išimtis, bet ir pateikia kontekstą:

  • Correlation-ID: viena užklausos ID, kurią sukuri kiekvienam kvietimui ir išlieką per kiekvieną pakartotinį bandymą.
  • Bandyto numeris ir vėlinimas (backoff).
  • HTTP-statusas ir pasirinktos antraštės (ypač Retry-After, RateLimit-Header, jei yra).
  • Trukmė vienam bandymui ir bendra trukmė.
  • Endpoint (host + kelias), bet jokios jautrios informacijos žurnale (tokenai, asmens duomenys).

Techniniams vadovams tai taip pat yra svertas ribų sureguliavimui: matote, ar laiko limitai „visada pasireiškia prie 3 sekundžių“ (greičiausiai per trumpi) arba ar 429 pasirodo bangomis (per didelė paralelizacija, per silpnas backoff arba trūksta kliento pusės rate-limitų).

Tipinės logavimo spąstai

  • Per daug duomenų (Payload): visų JSON body pilnas įrašymas į žurnalą gali atrodyti naudingas, bet sprogsta prie failų/priedų ir sukelia duomenų apsaugos problemas. Geriau: hash/dydis, Content-Type, ir esant reikalui tikslinis debug-logging per feature-flag.
  • Nėra skirtumo tarp timeout ir cancel: nutrauktas kvietimas nėra klaida to pačio tipo kaip timeout. Atskirkite juos, kitaip administratoriai gaudys fantomines klaidas.
  • Pakartojimas užgniaužia pradinę priežastį: jei bandymas 1 turėjo TLS klaidą, o bandymas 2 pavyko, vis tiek norėsite žinoti, kad buvo TLS nestabilumas. Tai ankstyvas įspėjimo signalas.

Kliento pusės Rate-Limiting: Wenn du die Last selbst steuern musst

429 yra serverio reakcija. Daugelyje scenarijų prasminga riboti jau kliento pusėje, prieš sukeliant 429. Tai ypač aktualu, kai tu:

  • turi batch-job’us (pvz. duomenų sinchronizacija naktį) ir API leidžia tik X užklausų per minutę.
  • naudoji kelis worker’us / gijas ir užklausas siunči lygiagrečiai.
  • turi kelias proceso instancijas (pvz. terminalo serveris arba kelios paslaugos).

Praktiškai tai reiškia: įdiegiate nedidelį Rate-Limiterį (pvz. Token-Bucket) kiekvienam tiksliniam sistemai arba API raktui. Tai sumažina 429, stabilizuoja pralaidumą ir padaro vykdymo trukmes labiau planuojamas. Operacijoms ir pajėgumų planavimui tai dažnai vertingiau nei „dar vienas retry“.

Svarbu: Rate-Limiter ir Backoff papildo vienas kitą

Rate-Limiter palaiko jus normaliu režimu žemiau limito. Backoff yra reakcija, kai vis tiek gaunate 429 arba laikiną perkrovą. Turint tik Backoff, nuolat „važiuosite į sieną“ ir galiausiai sulėtėsite. Turint tik Rate-Limiter, blogai reaguosite į netikėtus limitus ar bendras kvotas (pvz., kai kelios sistemos naudoja tą patį API raktą).

Sauga ir atitiktis: Pakartotiniai bandymai neturi užmaskuoti autentifikacijos problemų

Įmonėse autentifikacija ir autorizacija dažnai yra dažniausia „klaida“ po diegimo: pasibaigę tokenai, neteisingai sukonfigūruoti kliento kredencialai, trūkstamos proxy išimtys. Pakartotiniai bandymai čia nieko neišsprendžia ir gali būti kenksmingi, nes pildo žurnalus ir sukelia blokavimo mechanizmus (pvz., paskyrų užrakinimą, rate-limits prie autentifikacijos endpoint’ų).

Praktinė taisyklė: 401/403 jokiu būdu nebandyti iš naujo (išskyrus, jei turite sąmoningą token-refresh tvarką). Jei įdiegiate token-refresh, atskirkite tai aiškiai nuo retry mechanizmo: pirmiausia atnaujinkite tokeną, tada vieną kartą išsiųskite užklausą iš naujo. Ir užfiksuokite žurnale, kad įvyko refresh.

Kada pastangos atsiperka – ir kada ne

Patvarūs pakartotiniai bandymai ir backoff nėra savitikslis. Jie ypač apsimoka, kai bent vienas iš šių punktų galioja:

  • Integracija yra verslui kritinė (pvz., užsakymų registravimas, siuntimas, faktūravimas).
  • API yra išorinė arba vidinėje aplinkoje veikianti tik „best effort“ principu ir jūs neturite pilnos kontrolės.
  • Susiduriate su apkrovos pikais (pvz., užduočių langai, mėnesio uždarymas) ir norite užtikrinti stabilų veikimą.
  • Paleidžiate kaip servisą/daemoną ir jis turi būti planuotai ir tvarkingai sustabdomas.

Mažiau apsimoka, jei sąsajoje turite tik „patvirtinimo-GET“ užklausas ir naudotojas vis tiek vėl paspaudžia, arba jei dirbate vidinėje, labai stabilioje aplinkoje be kvotų ir klaidos akimirksniu matomos. Net ir tokiu atveju tvarkingi laiko limitai ir žurnavimas beveik visada yra prasmingi.

Pragmatiškas kontrolinis sąrašas produkcinio Delphi-REST kliento veikimui

  • Timeouts: konfigūruojami kiekvienam endpointui, parinkti realistiniu būdu, apibrėžtas bendras laiko biudžetas.
  • Retry-Policy: priklauso nuo HTTP metodo ir idempotencijos, negalima taikyti visiems atvejams vienodai.
  • 429-Handling: įvertinti Retry-After, backoff su jitter, stebėti paraleliškumą.
  • Abbruchpfad: backoff laukimas turi būti nutraukiamas (serviso sustabdymas, vartotojo atšaukimas).
  • Logging: Correlation-ID, bandymas, uždelsimas, trukmė, statusas/antraštės – be slaptų duomenų.
  • Optional: kliento pusės greičio ribotuvas partijiniam/paraleliniam veikimui.

Išvada: Atsparumas yra elgsena, ne vieno viską gaunančio Exception bloko naudojimas

Su RESTClient’u Delphi greitai įgyvendinsite veikiančius REST kvietimus. Tačiau į gamybą tinkamai pritaikyta sistema gaunama tik tada, kai laiko limitai apibrėžiami sąmoningai, pakartotiniai bandymai apsaugomi pagal sritį (idempotencija!), o 429 tipo Rate-Limit’ai gerbiami naudojant backoff ir jitter. Tam skirtas kodas nėra sudėtingas, bet jis turi būti centralizuotas, konfigūruojamas ir tinkamai stebimas. Būtent tada pastangos atsiperka: mažiau „sporadinių“ incidentų, geresnė diagnostika eksploatacijoje ir integracijos, kurios net ir apkrovos metu nesutrinka.

Jei norite tvarkingai įdiegti tokią retry-/backoff politiką esamoms Delphi programoms arba tinkamai ją paruošti naujai integracijai: susisiekite.

Šiam klausimui taip pat svarbūs Delphi Restclient timeoutai ir retry strategija Delphi. Šis įrašas aiškiai išdėsto šiuos aspektus ir parodo, kas svarbu kasdieniame darbe.

Aptarti projektą ar modernizacijos užduotį su Net-Base.

Sekantis žingsnis

Kai iš temos tampa realus projektas, architektūrą, esamą aplinką ir eksploatavimą reikėtų anksti nagrinėti kartu.

Mes padedame ne tik pavienėse užklausose, bet ir tuomet, kai iš šaltinio kodo fragmentų, paveldėtų temų ar portalo idėjų turi tapti patikimas įmonės projektas.

  • Esama padėtis, tikslinis vaizdas ir techninės rizikos vertinami kartu.
  • REST, duomenų prieiga, portalai ir diegimas nebus atidedami į vėlesnes stadijas.
  • Jūs anksti matote, kuris kelias yra ekonomiškai ir įmonės veiklos požiūriu tvarus.

Pasidalinti įrašu

Tiesiogiai pasidalinti šiuo įrašu

LinkedIn, X, XING, Facebook, WhatsApp ir el. paštas yra iš karto prieinami. Instagramui parengiame nuorodą ir trumpą tekstą nedelsiant.

El. paštas

Instagram atidaromas naujame skirtuke. Nuoroda ir trumpas tekstas iš anksto nukopijuojami į iškarpinę.