Jak zbudować stabilnego klienta HTTP z rotacją IP: poradnik krok po kroku dotyczący obsługi 429, backoff i timeoutów
Spis treści
- Wprowadzenie: dlaczego 429 to nie błąd, a sygnał
- Przygotowanie wstępne
- Podstawowe pojęcia prostym językiem
- Krok 1: prawidłowo konfigurujemy timeouity
- Krok 2: budujemy retransmisje z wykładniczym backoffem i jitterem
- Krok 3: ograniczamy współbieżność
- Krok 4: reagujemy właśnie na kod 429
- Krok 5: dodajemy circuit breaker i zarządzaną degradację
- Sprawdzenie wyniku: jakie metryki zliczać
- Typowe błędy i ich rozwiązania
- Gotowe fragmenty kodu
- Dodatkowe możliwości i optymalizacja
- Faq: często zadawane pytania
- Podsumowanie
Wyobraź sobie: napisałeś klienta, który wysyła zapytania do strony i wszystko działa. A potem nagle pojawiają się błędy, workerzy zawieszają się, a serwer odpowiada tajemniczym kodem 429. Brzmi znajomo? W takim razie ten poradnik jest dla Ciebie. Omówimy, jak zbudować klienta HTTP, który nie panikuje przy pierwszym problemie, ale zachowuje się grzecznie i stabilnie.
Wprowadzenie: dlaczego 429 to nie błąd, a sygnał
Wielu programistów widzi kod 429 i myśli: zepsuło się. W rzeczywistości serwer mówi ci bardzo konkretną rzecz: wysyłasz zbyt wiele zapytań, zwolnij. To nie jest odmowa ani trwałe zablokowanie. To prośba o zmniejszenie tempa. A jeśli odpowiednio ją usłyszysz, twój klient stanie się niezawodny.
Co otrzyma czytelnik na końcu
Pod koniec tego poradnika będziesz miał gotowego, działającego klienta HTTP, który potrafi kilka ważnych rzeczy. Poprawnie obsługuje kod 429 i respektuje nagłówek Retry-After. Używa wykładniczego backoffu z jitterem, aby nie wywoływać burzy powtórzeń. Ogranicza współbieżność, aby nie zalewać docelowego serwera. I nie zawiesza się dzięki odpowiednim timeoutom.
Otrzymasz gotowe fragmenty kodu w trzech językach: Python (przez bibliotekę httpx i przez urllib3 Retry), Node.js i Go. Każdy fragment możesz wstawić do swojego projektu i dostosować do zadania.
Dla kogo jest ten poradnik
Poradnik jest przeznaczony dla początkujących programistów, którzy już potrafią wykonywać proste zapytania HTTP, ale jeszcze nie mieli do czynienia z obciążeniem produkcyjnym. Jednocześnie zawiera bloki dla zaawansowanych: circuit breaker, metryki, zarządzana degradacja. Jeśli piszesz parser, integrację z zewnętrznym API lub usługę, która łączy się z zewnętrznymi zasobami, ten materiał zaoszczędzi Ci wiele nieprzespanych nocy.
Co trzeba wiedzieć wcześniej
Wystarczy rozumieć, czym jest zapytanie HTTP i odpowiedź HTTP. Wskazane jest znać, czym są kody statusu (np. 200 to sukces, a 404 – strona nie znaleziona). Przyda się podstawowa znajomość przynajmniej jednego z języków: Python, JavaScript lub Go. Głęboka wiedza o sieciach nie jest wymagana – wszystko wyjaśnimy prostymi słowami.
Ile czasu zajmie
Przeczytanie i zrozumienie teorii – około 40 minut. Złożenie podstawowego klienta krok po kroku – około godziny. Pełna implementacja ze wszystkimi zabezpieczeniami, metrykami i testami – około trzech godzin. Nie spiesz się: lepiej powoli zrozumieć każdy krok, niż szybko skopiować kod, którego nie rozumiesz.
Rada: Czytaj poradnik z otwartym edytorem kodu. Od razu testuj przykłady na testowym endpointcie, a nie na żywym serwisie produkcyjnym.
Przygotowanie wstępne
Zanim napiszesz kod, przygotujemy środowisko pracy. Zajmie to trochę czasu, ale uchroni Cię przed późniejszym zamieszaniem.
Niezbędne narzędzia
- Jeden z języków i jego środowisko: Python 3.11 lub nowszy, albo Node.js 20 lub nowszy, albo Go 1.22 lub nowszy.
- Edytor kodu – dowolny, np. VS Code.
- Terminal do uruchamiania skryptów.
- Dostęp do internetu do testowego serwisu HTTP, który potrafi zwracać różne kody odpowiedzi.
Co zainstalować dla Pythona
- Sprawdź wersję Pythona komendą w terminalu: wpisz python --version i naciśnij Enter.
- Utwórz wirtualne środowisko komendą python -m venv venv.
- Aktywuj je: w Windows komendą venv\Scripts\activate, w macOS i Linux komendą source venv/bin/activate.
- Zainstaluj biblioteki komendą pip install httpx urllib3 requests.
Co zainstalować dla Node.js
- Sprawdź wersję komendą node --version.
- Utwórz folder projektu i wejdź do niego.
- Zainicjuj projekt komendą npm init -y.
- Od Node.js 20 wbudowany fetch jest dostępny bez instalacji, dodatkowe pakiety dla podstawowego klienta nie są potrzebne.
Co zainstalować dla Go
- Sprawdź wersję komendą go version.
- Utwórz folder i zainicjuj moduł komendą go mod init myclient.
- Standardowa biblioteka net/http wystarczy, zewnętrzne pakiety nie są konieczne.
Kopie zapasowe i bezpieczeństwo
⚠️ Uwaga: Nigdy nie testuj nowego klienta od razu na ważnym serwisie produkcyjnym. Najpierw użyj testowego endpointa lub lokalnego serwera-zastępczego, który kontrolujesz. W przeciwnym razie agresywne retransmisje mogą zaszkodzić innemu serwisowi i doprowadzić do twojego zablokowania.
Jeśli ulepszasz istniejący projekt, zrób kopię pliku lub utwórz osobną gałąź w systemie kontroli wersji. Wtedy zawsze będziesz mógł cofnąć zmiany.
✅ Sprawdzenie: Zainstalowałeś wybrany język, utworzyłeś projekt i upewniłeś się, że testowy skrypt uruchamia się bez błędów. Teraz można przejść do teorii.
Podstawowe pojęcia prostym językiem
Aby pewnie budować klienta, trzeba zrozumieć kilka kluczowych terminów. Omówimy je bez skomplikowanych słów.
Co oznaczają kody 403, 407, 429 i 503
Te cztery kody łatwo pomylić, ale zachowują się inaczej i leczy się je też inaczej.
- Kod 429 Too Many Requests – serwer mówi, że przekroczyłeś limit zapytań. To tymczasowe. Trzeba zwolnić i powtórzyć później.
- Kod 403 Forbidden – dostęp zabroniony. Często nie chodzi o prędkość, ale o uprawnienia: nieprawidłowy klucz, brak autoryzacji, ograniczenie regionalne. Powtarzanie zapytania bez zmian zwykle jest bezcelowe.
- Kod 503 Service Unavailable – serwer tymczasowo przeciążony lub w trakcie konserwacji. Podobnie jak 429, to tymczasowe i powtórzenie później może pomóc.
- Kod 407 Proxy Authentication Required – i tutaj ważny niuans. Ten kod pochodzi nie od docelowej strony, ale od serwera proxy. Oznacza, że proxy wymaga autoryzacji, a Ty jej nie przekazałeś lub przekazałeś nieprawidłowo.
⚠️ Uwaga: Kodu 407 nie można leczyć rotacją IP ani backoffem. To błąd konfiguracji twojego klienta, a konkretnie nieprawidłowe dane uwierzytelniające dla proxy. Sprawdź login, hasło i format ciągu połączenia. Żadne powtórzenia nie pomogą, dopóki nie poprawisz autoryzacji.
Różnica między 429 a 403
Zapamiętaj prostą zasadę. 429 dotyczy ilości: robisz coś zbyt często. 403 dotyczy prawa: w ogóle nie wolno ci. Przy 429 powtórzenie po przerwie rozwiązuje problem. Przy 403 powtórzenie bez zmiany warunków go nie rozwiąże – trzeba zmienić klucz, nagłówki lub podejście.
Nagłówki Retry-After i X-RateLimit
Grzeczne serwery podpowiadają, kiedy można wrócić. Nagłówek Retry-After mówi, po ilu sekundach warto powtórzyć zapytanie. Czasami jest to liczba sekund, czasami konkretna data. Twój klient ma obowiązek respektować ten nagłówek: jeśli serwer kazał czekać 10 sekund, powtórzenie po 1 sekundzie tylko pogorszy sytuację.
Grupa nagłówków X-RateLimit informuje o limitach: ile zapytań ci wolno, ile pozostało i kiedy licznik zostanie zresetowany. Na przykład X-RateLimit-Remaining pokazuje pozostałą liczbę. Jeśli jest bliska zera, warto wcześniej zmniejszyć tempo, nie czekając na 429.
Jak działają limity: token bucket i przesuwne okno
Serwery liczą twoje zapytania na dwa popularne sposoby.
Token bucket (wiadro z tokenami) działa tak. Wyobraź sobie wiadro, do którego stale kapają tokeny ze stałą prędkością. Każde zapytanie zabiera jeden token. Jeśli tokenów nie ma – zapytanie jest odrzucane z kodem 429. Taki schemat dopuszcza krótkie skoki: jeśli długo milczałeś, wiadro się napełniło i możesz wysłać serię zapytań od razu.
Przesuwne okno (sliding window) liczy liczbę zapytań w ostatnim przedziale, np. minucie. Gdy przekroczysz limit w tym oknie – otrzymujesz 429. Tutaj skoki są karane surowiej.
Dlaczego współbieżność to też limit
Wielu zapomina: limit dotyczy nie tylko częstotliwości, ale także liczby równoczesnych połączeń. Jeśli otwierasz 500 równoległych zapytań, serwer może odebrać to jako atak, nawet jeśli ogólna liczba na minutę jest niewielka. Współbieżność należy ograniczać równie ściśle jak częstotliwość.
Rada: Zanim zbudujesz klienta, poznaj limity docelowego serwisu z jego dokumentacji. Znajomość dokładnych liczb uchroni cię przed domysłami i zbędnymi 429.
✅ Sprawdzenie: Rozumiesz różnicę między 429, 403, 407 a 503, wiesz o Retry-After i masz pojęcie, jak serwer liczy twoje zapytania. Świetnie, przechodzimy do praktyki.
Krok 1: prawidłowo konfigurujemy timeouity
Cel etapu: sprawić, aby żadne zapytanie nie mogło zawisnąć na zawsze i zablokować workera.
Dlaczego klient bez timeoutu jest niebezpieczny
Klient bez timeoutu to bomba z opóźnionym zapłonem. Jeśli serwer przestanie odpowiadać, twoje zapytanie będzie czekać w nieskończoność. Jedno zawieszone zapytanie blokuje jednego workera. Dziesięć zawieszonych zapytań – i cały pul workerów jest zajęty, nowe zadania nie są przetwarzane, serwis praktycznie stoi. Timeout to twoja pierwsza linia obrony.
Cztery rodzaje timeoutów
Prawidłowy klient rozróżnia kilka timeoutów, a nie ustawia jeden wspólny na wszystko.
- Connect timeout (połączenie) – jak długo czekać na nawiązanie połączenia z serwerem. Jeśli serwer jest niedostępny, dowiesz się o tym szybko.
- Read timeout (odczyt) – jak długo czekać na dane po wysłaniu zapytania. Chroni przed serwerem, który przyjął zapytanie, ale milczy.
- Write timeout (zapis) – jak długo czekać na wysłanie treści zapytania. Aktualne przy dużych przesyłkach.
- Ogólny timeout (total) – maksymalny czas na całe zapytanie, obejmujący wszystkie fazy.
Jakie wartości przyjąć na start
Uniwersalnych liczb nie ma, ale są rozsądne wartości początkowe. Dla connect przyjmij 3-5 sekund: połączenie zwykle nawiązuje się szybko. Dla read przyjmij 10-30 sekund w zależności od tego, jak szybko serwis zwraca dane. Ogólny timeout ustaw tak, aby pokrywał najdłuższe rozsądne zapytanie, np. 30-60 sekund.
⚠️ Uwaga: Nigdy nie ustawiaj ogromnych timeoutów, np. 300 sekund na wszystkie zapytania. To maskuje problemy i tworzy kolejkę zawieszonych operacji. Lepiej szybko upaść i powtórzyć, niż długo czekać bez efektu.
Konfiguracja krok po kroku
- Określ, ile zwykle trwa udane zapytanie do twojego serwisu. Zmierz kilka razy.
- Ustaw read timeout na około dwa razy większy niż średni czas odpowiedzi.
- Ustaw connect timeout na 3-5 sekund.
- Ustaw ogólny timeout jako sumę rozsądnych faz plus niewielki zapas.
- Uruchom testowe zapytanie i upewnij się, że kończy się, a nie wisi.
Rada: Jeśli twój serwis czasami zwraca duże pliki, a czasami małe odpowiedzi, utwórz różne profile timeoutów dla różnych typów zapytań. Jeden rozmiar nie pasuje do wszystkich.
Oczekiwany rezultat: przy próbie dostępu do celowo wolnego lub niedostępnego adresu twój klient kończy próbę po zadanym czasie z zrozumiałym błędem timeoutu, a nie wisi wiecznie.
✅ Sprawdzenie: Wyślij zapytanie na adres, który nie odpowiada (np. nieistniejący port). Klient powinien zwrócić błąd timeoutu mniej więcej w zadanym czasie. Jeśli wisi dłużej – timeout jest skonfigurowany nieprawidłowo.
Krok 2: budujemy retransmisje z wykładniczym backoffem i jitterem
Cel etapu: nauczyć klienta mądrego powtarzania zapytań, bez szkody dla siebie i serwera.
Co w ogóle można powtarzać: idempotentność
Zanim powtórzysz zapytanie, zapytaj siebie: czy bezpiecznie jest wykonać je dwukrotnie? Ta właściwość nazywa się idempotentnością. Zapytanie jest idempotentne, jeśli ponowne wykonanie daje ten sam wynik i nie powoduje skutków ubocznych.
- GET, HEAD, PUT, DELETE są zwykle idempotentne. Powtórzenie ich jest bezpieczne.
- POST zwykle nie jest idempotentny. Powtórzenie może stworzyć duplikat zamówienia, drugą płatność, duplikujący wpis.
⚠️ Uwaga: Nigdy nie powtarzaj zapytań POST na ślepo. Ponowne wysłanie nieidempotentnego zapytania może doprowadzić do podwójnego obciążenia pieniędzy lub duplikowania danych. Jeśli potrzebujesz powtórzenia POST, użyj klucza idempotentności (Idempotency-Key), który serwer rozpozna i nie wykona operacji dwukrotnie.
Ile razy powtarzać
Nieskończone powtórzenia to zło. Rozsądny limit to od 3 do 5 prób. Jeśli po pięciu próbach zapytanie się nie powiodło, oznacza to, że problem jest poważniejszy niż tymczasowa usterka i należy go zalogować i przetwarzać osobno.
Co to jest wykładniczy backoff
Backoff to przerwa między powtórzeniami. Wykładniczy oznacza, że przerwa rośnie wielokrotnie z każdą próbą. Na przykład: pierwsza przerwa 1 sekunda, druga 2 sekundy, trzecia 4, czwarta 8. Wzór jest prosty: opóźnienie bazowe mnoży się przez dwa do potęgi numeru próby.
Dlaczego akurat tak? Jeśli serwer jest przeciążony, krótkie częste powtórzenia tylko go dobiją. Rosnące przerwy dają serwerowi czas na dojście do siebie.
Dlaczego bez jittera powstaje burza powtórzeń
Wyobraź sobie, że tysiąc klientów jednocześnie otrzymało 429. Wszyscy czekają dokładnie 1 sekundę, potem dokładnie 2, potem dokładnie 4. I wszyscy powtarzają w tym samym momencie. Powstaje synchroniczna burza: serwer ponownie otrzymuje tysiąc zapytań naraz i ponownie zwraca 429. Problem nie jest rozwiązywany, tylko zapętla się.
Rozwiązanie – jitter, czyli losowy dodatek do przerwy. Zamiast dokładnie 2 sekund jeden klient czeka 1.7, inny 2.3, trzeci 1.9. Powtórzenia rozkładają się w czasie, a serwer rozładowuje się płynnie.
Jak respektować Retry-After
Jeśli serwer przysłał nagłówek Retry-After, jest ważniejszy niż twoja formuła backoffu. Zasada jest prosta: bierz maksimum z obliczonej przerwy i wartości Retry-After. Nigdy nie powtarzaj wcześniej, niż prosił serwer. To rażące naruszenie uprzejmości, które doprowadzi do nowych 429.
Implementacja logiki retransmisji krok po kroku
- Sprawdź, czy zapytanie jest idempotentne. Jeśli nie i nie ma klucza idempotentności – nie powtarzaj.
- Sprawdź kod odpowiedzi. Powtarzaj tylko przy 429, 503 i błędach sieciowych (timeout, zerwanie połączenia).
- Zwiększ licznik prób. Jeśli przekroczył limit – zakończ i zwróć błąd.
- Oblicz bazową przerwę według wzoru wykładniczego wzrostu.
- Dodaj losowy jitter do przerwy.
- Jeśli przyszedł Retry-After, weź większą z dwóch wartości.
- Odczekaj obliczony czas i powtórz zapytanie.
Rada: Ograniczaj maksymalną przerwę od góry, np. 30 lub 60 sekundami. W przeciwnym razie przy piątej próbie backoff może urosnąć do nieprzyzwoicie dużych wartości, a użytkownik będzie czekał zbyt długo.
Oczekiwany rezultat: przy kodzie 429 klient robi przerwę, powtarza zapytanie, a przerwy między powtórzeniami rosną i nieznacznie różnią się za każdym razem.
✅ Sprawdzenie: Skonfiguruj testowy serwer, aby zwracał 429 kilka razy z rzędu, a potem 200. Twój klient powinien pomyślnie otrzymać końcową odpowiedź, a w logach zobaczysz rosnące przerwy z rozrzutem.
Krok 3: ograniczamy współbieżność
Cel etapu: nie pozwolić klientowi zalać serwera lawiną równoczesnych zapytań.
Co to jest semafor prostymi słowami
Semafor to licznik zezwoleń. Wyobraź sobie szatnię z ograniczoną liczbą wieszaków. Dopóki jest wolny wieszak, wieszasz płaszcz. Jeśli wszystkie są zajęte – czekasz, aż ktoś zwolni. Semafor przepuszcza ograniczoną liczbę zadań jednocześnie, a pozostałe trzyma w kolejce.
Kolejka zadań
Wszystkie zapytania do wykonania są umieszczane w kolejce. Workerzy pobierają zadania z kolejki w miarę zwalniania się. Daje to pełną kontrolę nad tempem: ilu workerów, tyle maksymalnie równoległych zapytań.
Limit na host
Ważny niuans: limit należy utrzymywać oddzielnie dla każdego hosta. Jeśli pracujesz z wieloma usługami, globalny limit na wszystko na raz nie jest optymalny. Jeden wolny host nie powinien blokować zapytań do innego. Ustaw osobny limit dla każdej domeny.
Pula połączeń i keep-alive
Każde nowe połączenie TCP kosztuje czas: uzgadnianie, ustanawianie bezpiecznego kanału. Keep-alive pozwala ponownie użyć połączenia dla kilku zapytań z rzędu. Oszczędza to czas i zasoby serwera. Pula połączeń przechowuje otwarte połączenia w gotowości. Dostosuj rozmiar puli do swojego limitu współbieżności.
⚠️ Uwaga: Nie myl rozmiaru puli połączeń z limitem współbieżności. Pula może być nieco większa od limitu dla zapasu, ale jeśli pula jest ogromna, a limit mały – niepotrzebnie utrzymujesz otwarte połączenia. Zachowaj rozsądną równowagę.
Konfiguracja ograniczenia krok po kroku
- Określ bezpieczną liczbę równoczesnych zapytań na host. Zacznij od małej, np. 5-10.
- Utwórz semafor z tą liczbą zezwoleń.
- Przed każdym zapytaniem żądaj zezwolenia od semafora.
- Po zakończeniu zapytania, niezależnie od sukcesu, zwolnij zezwolenie.
- Skonfiguruj pulę połączeń z keep-alive na ten sam rząd wartości.
- Stopniowo zwiększaj limit, obserwując udział 429. Gdy tylko rośnie – zatrzymaj się.
Rada: Zwolnij zezwolenie semafora w bloku finally lub jego odpowiedniku. W przeciwnym razie przy błędzie zezwolenie nie wróci, licznik wycieknie i z czasem klient zatrzyma się na dobre.
Oczekiwany rezultat: niezależnie od liczby zadań w kolejce, liczba równoczesnych zapytań do hosta nie przekracza ustawionego limitu.
✅ Sprawdzenie: Umieść w kolejce 100 zadań z limitem 5. W logach lub monitorze połączeń powinieneś widzieć nie więcej niż 5 aktywnych zapytań w dowolnym momencie.
Krok 4: reagujemy właśnie na kod 429
Cel etapu: zbudować prawidłową reakcję na sygnał przeciążenia i zrozumieć, kiedy zmiana IP jest właściwa.
Trzy działania przy 429
Gdy nadchodzi 429, masz trzy narzędzia i należy je stosować w połączeniu.
- Zwolnij – zmniejsz ogólne tempo zapytań, a nie tylko zrób przerwę dla jednego zapytania. To kluczowe: 429 to sygnał, że całe twoje tempo jest zbyt wysokie.
- Zmień IP – jeśli pracujesz przez rotację adresów IP, zmiana adresu może pomóc, gdy limit jest przypisany do konkretnego adresu. Ale to nie panaceum.
- Odłóż zadanie – zwróć zapytanie do kolejki z opóźnieniem, aby wykonać je później, gdy limity się odnowią.
⚠️ Uwaga: Zmiana IP nie unieważnia uprzejmości. Jeśli limit dotyczy nie IP, a konta lub klucza, żadna rotacja nie pomoże – i tak trafisz na 429. Nie zamieniaj rotacji w sposób na obejście zasad: respektuj limity serwisu i Retry-After w każdym przypadku.
Macierz działań według kodów odpowiedzi
Miej pod ręką prostą tabelę decyzji. Oto co robić przy każdym kodzie.
- 200-299 Sukces – przetwórz odpowiedź, zwolnij zasoby, pobierz następne zadanie.
- 429 Too Many Requests – zwolnij tempo, respektuj Retry-After, powtórz z backoffem, w razie potrzeby odłóż zadanie lub zmień IP.
- 503 Service Unavailable – powtórz z backoffem, respektuj Retry-After, ale nie zmieniaj IP: problem leży po stronie serwera.
- 403 Forbidden – nie powtarzaj na ślepo. Sprawdź autoryzację, nagłówki, uprawnienia. Zaloguj do analizy.
- 407 Proxy Authentication Required – popraw dane uwierzytelniające proxy. Nie powtarzaj ani nie rotuj do czasu poprawienia konfiguracji.
- 400, 404, 422 błędy klienta – nie powtarzaj. To błąd twojego zapytania, powtórzenie nic nie zmieni.
- 500, 502, 504 błędy serwera – ostrożnie powtórz z backoffem niewielką liczbę razy.
- Błędy sieciowe i timeouity – powtórz z backoffem, jeśli zapytanie jest idempotentne.
Implementacja reakcji na 429 krok po kroku
- Po otrzymaniu 429 natychmiast przestań zwiększać tempo.
- Odczytaj nagłówek Retry-After, jeśli istnieje.
- Oblicz przerwę jako maksimum z backoffu i Retry-After.
- Jeśli limit prawdopodobnie dotyczy IP i masz rotację – zmień adres przed powtórzeniem.
- Jeśli próby się wyczerpały – odłóż zadanie z powrotem do kolejki z dużym opóźnieniem.
- Tymczasowo zmniejsz ogólny limit współbieżności, aby dać serwerowi odetchnąć.
Rada: Prowadź osobny licznik udziału 429 w ostatniej minucie. Jeśli rośnie, automatycznie zmniejszaj tempo, zanim sytuacja stanie się krytyczna. To się nazywa adaptacyjne ograniczanie.
Oczekiwany rezultat: przy serii 429 klient płynnie zmniejsza tempo, respektuje Retry-After i ostatecznie pomyślnie kończy zapytania, nie wywołując burzy.
✅ Sprawdzenie: Zasymuluj wzrost 429 na testowym serwerze. Klient powinien zmniejszyć aktywność, a nie zwiększać powtórzeń. Udział udanych odpowiedzi po przerwie powinien się odnowić.
Krok 5: dodajemy circuit breaker i zarządzaną degradację
Cel etapu: dać klientowi bezpiecznik chroniący zarówno ciebie, jak i serwer w przypadku długotrwałych problemów.
Co to jest circuit breaker
Circuit breaker to bezpiecznik, jak w skrzynce elektrycznej. Jeśli błędy płyną strumieniem, rozłącza obwód: przestaje przepuszczać zapytania do problematycznego serwisu na pewien czas. Chroni to serwer przed dobiciem i twój klient przed bezsensownym marnowaniem zasobów.
Trzy stany bezpiecznika
- Closed (zamknięty) – normalna praca, zapytania przechodzą. Klient liczy błędy.
- Open (otwarty) – zbyt wiele błędów, zapytania są blokowane od razu bez wysyłania do serwera. Utrzymuje się przez zadany czas.
- Half-open (półotwarty) – tryb próbny. Klient przepuszcza kilka zapytań, aby sprawdzić, czy serwis się odzyskał. Jeśli tak – wraca do closed, jeśli nie – znowu open.
Zarządzana degradacja zamiast całkowitego zatrzymania
Gdy serwis jest niedostępny, nie trzeba wszystkiego rujnować. Zarządzana degradacja to zdolność do pracy gorzej, ale kontynuowania. Przykłady: zwróć dane z cache zamiast świeżych, pokaż okrojony wynik, odłóż nieobowiązkowe zadania, zwróć zrozumiały placeholder zamiast błędu.
Rada: Zawsze myśl, co pokazać użytkownikowi lub systemowi, gdy zewnętrzny serwis leży. Placeholder z sensownym komunikatem jest lepszy niż zawieszenie lub stack trace.
Konfiguracja circuit breakera krok po kroku
- Ustaw próg błędów, przy którym bezpiecznik się rozłącza, np. 50% niepowodzeń w oknie 20 zapytań.
- Ustaw czas, na który obwód jest otwarty, np. 30 sekund.
- Licz sukcesy i porażki w przesuwnym oknie.
- Po przekroczeniu progu przełącz bezpiecznik w stan open.
- Po upływie czasu przełącz go w half-open i przepuść kilka próbnych zapytań.
- Na podstawie wyników prób wróć do closed lub ponownie do open.
⚠️ Uwaga: Nie myl circuit breakera z retransmisjami. Retransmisje powtarzają jedno zapytanie, a bezpiecznik zarządza całym strumieniem do serwisu. Razem są potężne, ale trzeba je skonfigurować spójnie, aby bezpiecznik nie otwierał się zbyt wcześnie z powodu normalnych pojedynczych błędów.
Oczekiwany rezultat: przy długotrwałej niedostępności serwisu klient przestaje go bombardować zapytaniami, szybko zwraca placeholder i okresowo sprawdza odzyskanie.
✅ Sprawdzenie: Ustaw testowy serwer jako niedostępny. Klient po serii niepowodzeń powinien przestać wysyłać zapytania (open), a po odzyskaniu serwera sam wrócić do normalnej pracy przez half-open.
Sprawdzenie wyniku: jakie metryki zliczać
Stabilności nie da się ocenić na oko. Potrzebne są liczby. Oto kluczowe metryki, które pokażą, czy klient stał się bardziej niezawodny.
Główne wskaźniki
- Udział udanych odpowiedzi (success rate) – procent zapytań zakończonych kodem 2xx. Im wyższy, tym lepiej. Dąż do stabilnie wysokiej wartości nawet pod obciążeniem.
- p95 opóźnienia – czas, w którym mieści się 95% zapytań. Ten wskaźnik jest bardziej uczciwy niż średnia, ponieważ pokazuje, jak czuje się większość, a nie tylko szczęśliwe zapytania.
- Udział 429 – procent odpowiedzi z kodem 429. Jeśli jest wysoki, wysyłasz zbyt agresywnie. Celem jest zminimalizowanie go.
- Liczba powtórzeń na zapytanie – pokazuje, jak trudno osiągnąć sukces. Wzrost oznacza problemy.
- Liczba otwarć circuit breakera – częste otwarcia sygnalizują niestabilność serwisu lub zbyt agresywne ustawienia.
Lista kontrolna gotowości
- Timeouity skonfigurowane dla wszystkich faz, żadne zapytanie nie wisi wiecznie.
- Retransmisje działają tylko dla idempotentnych zapytań i bezpiecznych kodów.
- Backoff rośnie wykładniczo i zawiera jitter.
- Retry-After jest zawsze respektowany.
- Współbieżność ograniczona semaforem dla każdego hosta.
- Pula połączeń z keep-alive skonfigurowana zgodnie z limitem.
- Reakcja na 429 zmniejsza tempo, a nie zwiększa powtórzeń.
- Macierz działań według kodów zaimplementowana.
- Circuit breaker chroni przed długotrwałymi awariami.
- Metryki są zbierane i dostępne do analizy.
Jak stwierdzić, że klient stał się stabilniejszy
Porównaj metryki przed i po udoskonaleniach przy tym samym obciążeniu. Stabilny klient wykazuje wysoki udział sukcesów, niski udział 429, stabilne p95 i brak zawieszonych workerów. Nawet gdy serwer kaprysi, twój serwis kontynuuje pracę bez kaskadowych awarii.
✅ Sprawdzenie: Przeprowadź test obciążenia na testowym endpointcie. Jeśli pod obciążeniem udział sukcesów pozostaje wysoki i nie ma zawieszeń – gratulacje, klient jest stabilny.
Typowe błędy i ich rozwiązania
Omówimy częste pułapki, na które wpada prawie każdy.
Błąd 1: retransmisje zwiększają obciążenie
Problem: serwer jest przeciążony, a twoje agresywne powtórzenia dobijają go ostatecznie. Przyczyna: powtórzenia bez backoffu i bez zmniejszania tempa. Rozwiązanie: dodaj wykładniczy backoff z jitterem, ogranicz liczbę prób, zmniejszaj ogólną współbieżność przy wzroście błędów.
Błąd 2: powtarzanie nieidempotentnych zapytań
Problem: podwójne zamówienia, wielokrotne obciążenia, duplikaty wpisów. Przyczyna: ślepe powtarzanie zapytań POST. Rozwiązanie: powtarzaj tylko metody idempotentne. Dla POST używaj klucza idempotentności, który serwer rozpozna i nie wykona operacji dwukrotnie.
Błąd 3: leczenie 429 niekończącą się zmianą IP
Problem: zmieniasz IP w kółko, a 429 nie znika. Przyczyna: limit nie dotyczy IP, ale klucza lub konta, albo po prostu wysyłasz zbyt dużo łącznie. Rozwiązanie: zmniejsz tempo i respektuj Retry-After. Rotacja IP to tylko jedno z narzędzi, a nie zastępstwo uprzejmości.
Błąd 4: synchroniczna burza powtórzeń
Problem: wszyscy klienci powtarzają w tych samych momentach, serwer znowu pada. Przyczyna: backoff bez jittera. Rozwiązanie: dodaj losowy składnik do każdej przerwy.
Błąd 5: zawieszone workery
Problem: serwis stopniowo przestaje przetwarzać zadania. Przyczyna: brak timeoutów, zapytania wiszą wiecznie. Rozwiązanie: skonfiguruj timeouity connect, read i ogólne dla wszystkich zapytań.
Błąd 6: wyciek zezwoleń semafora
Problem: z czasem klient przestaje wysyłać zapytania. Przyczyna: zezwolenie semafora nie jest zwalniane przy błędzie. Rozwiązanie: zwalniaj zezwolenie w bloku finally, aby zawsze się to działo.
Błąd 7: nieprawidłowa reakcja na 407
Problem: klient nieskończenie powtarza i rotuje IP, ale otrzymuje 407. Przyczyna: kod 407 pochodzi od proxy i oznacza błąd autoryzacji proxy, a nie problem serwisu. Rozwiązanie: sprawdź i popraw dane uwierzytelniające proxy. Powtórzenia są tu bezcelowe.
Gotowe fragmenty kodu
Poniżej opisy podejść na trzech stosach. Dostosuj do swojego projektu.
Python na httpx
Utwórz klienta httpx z jawnymi timeoutami przez obiekt Timeout, gdzie osobno ustawione są connect i read. Ustaw limity puli przez httpx Limits, podając maksymalną liczbę połączeń na host. Owiń wywołanie w pętlę powtórzeń: przy 429 i 503 czytaj Retry-After, oblicz przerwę jako maksimum z wykładniczego backoffu z jitterem i wartości Retry-After, następnie zrób przerwę przez asyncio sleep. Ogranicz współbieżność przez asyncio Semaphore, zwalniając go w bloku finally. Powtarzaj tylko metody idempotentne, ogranicz liczbę prób do pięciu.
Python na urllib3 Retry
Biblioteka urllib3 oferuje gotowy mechanizm. Utwórz obiekt Retry z parametrami: total określa liczbę prób, backoff_factor włącza wykładnicze przerwy, status_forcelist wymienia kody do powtórzenia, np. 429, 500, 502, 503, 504. Parametr respect_retry_after_header włącza respektowanie Retry-After. Przekaż ten Retry do PoolManager lub do adaptera requests przez HTTPAdapter. To najszybszy sposób na uzyskanie podstawowej stabilności bez pisania pętli ręcznie.
Node.js
Użyj wbudowanego fetch z AbortController do timeoutu: utwórz kontroler, ustaw setTimeout na abort, przekaż signal do fetch. Owiń wywołanie w funkcję z pętlą powtórzeń. Sprawdzaj response.status: przy 429 i 503 czytaj nagłówek Retry-After przez response.headers.get, oblicz przerwę z jitterem, czekaj przez promise z setTimeout. Do ograniczenia współbieżności użyj prostego semafora na promise'ach lub popularnej biblioteki ograniczającej. Utrzymuj liczbę równoczesnych promise'ów pod kontrolą przez kolejkę.
Go
W Go skonfiguruj http Client z polem Timeout dla ogólnego timeoutu i skonfiguruj Transport z parametrami MaxIdleConnsPerHost i IdleConnTimeout dla puli i keep-alive. Dla timeoutu połączenia użyj DialContext z net Dialer. Zaimplementuj pętlę powtórzeń: przy 429 i 503 czytaj nagłówek Retry-After, oblicz przerwę przez time Duration z wykładniczym wzrostem i losowym jitterem, czekaj przez time Sleep lub select z context. Ogranicz współbieżność przez buforowany kanał jako semafor: pisz do kanału przed zapytaniem, odczytuj z niego w defer po.
Rada: W każdym języku wynieś ustawienia (timeouity, liczbę prób, limit współbieżności) do konfiguracji, a nie koduj na sztywno. Dzięki temu dostosujesz zachowanie do każdego serwisu bez przepisywania kodu.
Dodatkowe możliwości i optymalizacja
Gdy podstawowy klient działa, można uczynić go jeszcze mądrzejszym.
Adaptacyjne ograniczanie tempa
Zamiast stałego limitu zrób go płynnym. Czytaj nagłówki X-RateLimit-Remaining i wcześniej zmniejszaj tempo, gdy pozostało mało. Unikniesz w ten sposób 429 jeszcze przed ich pojawieniem się.
Priorytety zadań
Nie wszystkie zapytania są równe. Utwórz kolejkę z priorytetami: ważne zadania wykonują się wcześniej, nieobowiązkowe są odkładane jako pierwsze przy degradacji.
Buforowanie
Dla idempotentnych zapytań GET dodaj cache z krótkim czasem życia. Zmniejsza to obciążenie serwera i twój udział 429 bez żadnych sztuczek.
Obserwowalność
Podłącz strukturalne logi i metryki. Loguj każde powtórzenie, każde otwarcie circuit breakera, każdą długą przerwę. W ten sposób szybko znajdziesz wąskie gardło podczas analizy incydentów.
Rada: Zacznij od prostego klienta i dodawaj zaawansowane funkcje w miarę rzeczywistej potrzeby. Przedwczesna złożoność jest równie szkodliwa jak jej brak.
FAQ: często zadawane pytania
Czy zawsze trzeba respektować Retry-After, nawet jeśli jest duży?
Tak. Jeśli Retry-After jest zbyt duży dla twojego scenariusza, lepiej odłożyć zadanie lub zwrócić zdegradowaną odpowiedź, niż powtarzać przed czasem. Ignorowanie Retry-After prawie zawsze prowadzi do nowych 429.
Czy można powtarzać zapytania POST?
Tylko ostrożnie. Jeśli operacja nie jest idempotentna, powtórzenie może stworzyć duplikat. Użyj klucza idempotentności, aby serwer sam uchronił cię przed podwójnym wykonaniem.
Jaką liczbę równoczesnych zapytań przyjąć na start?
Zacznij od małej, np. 5-10 na host, i zwiększaj, obserwując udział 429 i p95. Gdy 429 rośnie – znalazłeś sufit.
Czym 429 różni się od 503 w praktyce?
429 dotyczy twojego tempa: wysyłasz zbyt często. 503 dotyczy serwera: jest przeciążony lub w konserwacji. Przy 429 warto zmniejszyć tempo i ewentualnie zmienić IP. Przy 503 zmiana IP nie ma sensu, po prostu powtórz później.
Dlaczego mój klient czasami otrzymuje 407?
Kod 407 pochodzi od proxy i oznacza, że autoryzacja na proxy nie przeszła. Sprawdź login i hasło proxy. Rotacja IP i backoff tu nie pomogą – to błąd konfiguracji.
Ile prób powtórzenia uważa się za normę?
Zwykle od trzech do pięciu. Więcej rzadko ma sens: jeśli nie pomogło po pięciu próbach, problem jest poważniejszy niż tymczasowa usterka.
Po co jitter, skoro backoff i tak rośnie?
Bez jittera wiele klientów powtarza w tych samych momentach i tworzy synchroniczną burzę. Losowy rozrzut rozkłada powtórzenia w czasie i płynnie odciąża serwer.
Kiedy otwierać circuit breaker?
Gdy udział błędów w przesuwnym oknie przekracza zadany próg, np. połowę zapytań. Chroni to zarówno serwer, jak i ciebie przed bezsensownym marnowaniem zasobów.
Czy zmiana IP pomaga na 429?
Czasami, jeśli limit dotyczy IP. Ale jeśli limit dotyczy klucza lub konta, zmiana IP jest bezużyteczna. Zmiana IP nie zastępuje zmniejszenia tempa i respektowania Retry-After.
Co pokazać użytkownikowi, gdy serwis leży?
Zrozumiały placeholder, dane z cache lub okrojony wynik. To lepsze niż zawieszenie lub błąd techniczny na ekranie.
Podsumowanie
Przeszedłeś długą drogę. Przypomnijmy, co zbudowałeś. Skonfigurowałeś timeouity dla wszystkich faz, aby żadne zapytanie nie zawisło na zawsze. Dodałeś inteligentne retransmisje z wykładniczym backoffem i jitterem, które powtarzają tylko bezpieczne zapytania i respektują Retry-After. Ograniczyłeś współbieżność semaforem i skonfigurowałeś pulę połączeń z keep-alive. Opracowałeś prawidłową reakcję na 429 i stworzyłeś macierz działań według kodów odpowiedzi. Na koniec dodałeś circuit breaker i zarządzaną degradację.
Główna myśl całego poradnika jest prosta. 429 to nie błąd, a rozmowa. Serwer mówi ci, żebyś zwolnił, a grzeczny klient słucha. Stabilność rodzi się nie z agresji, ale z umiejętności przyhamowania we właściwym momencie.
Co robić dalej
Zbierz metryki na rzeczywistym obciążeniu i spójrz na udziały sukcesów i 429. Stopniowo dostosuj limity do każdego serwisu. Dodaj adaptacyjne ograniczanie tempa na podstawie nagłówków X-RateLimit. Wdróż buforowanie dla idempotentnych zapytań.
Gdzie się rozwijać
Przestudiuj osobno temat puli adresów IP i jej kondycji – to duży sąsiedni obszar, którego celowo nie poruszaliśmy tutaj. Zanurz się w obserwowalność: śledzenie, dashboardy, alerty. I koniecznie przeczytaj dokumentację serwisów, z którymi pracujesz: dokładne limity są zawsze lepsze od domysłów.
Świetnie sobie poradziłeś. Teraz masz klienta, który nie panikuje, ale zachowuje się stabilnie i grzecznie. To fundament, na którym buduje się niezawodne integracje. Powodzenia w twoich projektach.