Obserwowalność puli proxy: metryki, logi, alerty i diagnoza degradacji w minutę
Spis treści
- Wprowadzenie: bez metryk problem wygląda jak „wszystko nam zamula”
- Cztery sygnały dla proxy: skuteczność, opóźnienie, ponowne próby, ruch
- Co zapisywać w logu na każde żądanie — a czego zapisywać nie wolno
- Percentyle zamiast średniej: dlaczego średnia ukrywa problem
- Oznaczanie wymiarami: kraj, operator, typ proxy
- Alerty, które nie szumią: progi, okna, histereza
- Szybka diagnoza po dashboardzie: trzy typowe obrazy
- Typowe błędy, które zerują korzyść z obserwowalności
- Minimalny stos bez przekombinowania
- Przypadki: jak obserwowalność wygląda w praktyce
- Faq: częste pytania o obserwowalność puli proxy
- Podsumowanie: jedna minuta zamiast godziny
Wprowadzenie: bez metryk problem wygląda jak „wszystko nam zamula”
Znany scenariusz. Na czacie firmowym pojawia się wiadomość: „proxy zamulają”. Po pięciu minutach druga: „i błędów dużo”. Potem zaczyna się to, co inżynierowie nazywają archeologią. Ktoś otwiera konsolę i ręcznie puszcza curl przez trzy losowe adresy. Ktoś patrzy na wykres obciążenia procesora, który z proxy nie ma nic wspólnego. Ktoś pisze do wsparcia dostawcy maila w stylu „coś u was nie działa”, nie mając w ręku ani jednej liczby. Mija godzina. Okazuje się, że zdegradował się tylko jeden segment puli, tylko w jednym kraju, i zaczęło się to nie „teraz”, a dwa dni temu, po prostu nikt nie zauważył.
Różnica między zespołem, który na taką historię traci godzinę, a zespołem, który traci minutę, nie tkwi w doświadczeniu ani talencie. Różnica polega na tym, że ten drugi zespół z góry zdecydował, co będzie mierzył, co zapisze w logach i na co postawi alerty. Kiedy masz wykres wskaźnika sukcesu w podziale na kraj i operatora, zdanie „wszystko zamula” zamienia się w konkretne stwierdzenie: „od 14:32 p95 czasu do pierwszego bajtu przez proxy mobilne operatora A w regionie B wzrosło z 1,8 do 6,4 sekundy, wskaźnik błędów się nie zmienił, wskaźnik ponownych prób potroił się”. Z takim stwierdzeniem można już pracować: przenieść obciążenie, otworzyć ticket u dostawcy z danymi, a nie z emocjami, albo zrozumieć, że problem wcale nie jest w proxy, tylko w zasobie docelowym.
W tym poradniku przejrzymy obserwowalność wokół puli proxy od początku do końca. Dowiesz się, które cztery sygnały dają niemal całą użyteczną informację, jakie pola muszą znaleźć się w każdym wierszu logu, a jakie nie powinny trafić tam nigdy, dlaczego średni czas odpowiedzi kłamie i jak liczyć percentyle poprawnie, jak oznaczać dane wymiarami, żeby widzieć degradację pojedynczego wycinka, jak budować alerty, które odzywają się rzadko, ale celnie, i jak po dashboardzie w minutę rozróżnić trzy typowe obrazy problemów. Na końcu omówimy minimalny stos technologiczny, realne przypadki z liczbami i odpowiemy na częste pytania.
Od razu wyznaczmy granice. Wyboru konkretnego IP z puli, sprawdzania kondycji proxy i kwarantanny niesprawnych adresów tu szczegółowo nie omawiamy: temu poświęcony jest osobny materiał. Tutaj mowa tylko o dwóch rzeczach: pomiarze i alertowaniu. Wszystko, co opisano poniżej, ma zastosowanie w legalnych scenariuszach inżynierskich: monitoring cen na publicznych platformach, sprawdzanie wyświetlania reklam w różnych regionach, testowanie treści zależnych od geolokalizacji we własnych serwisach, zbieranie otwartych danych zgodnie z regulaminami serwisów.
Trochę podstaw: czym jest obserwowalność w kontekście proxy
Klasyczna definicja mówi, że obserwowalność (observability) to właściwość systemu, która pozwala zrozumieć jego stan wewnętrzny na podstawie zewnętrznych wyjść. W tłumaczeniu na język inżynierski: możesz zadać systemowi nowe pytanie, którego nie przewidziałeś wcześniej, i dostać odpowiedź bez wdrażania nowego kodu. Monitoring odpowiada na z góry znane pytania („czy usługa żyje?”), obserwowalność — na nieznane („dlaczego żądania do tej domeny przez mobilne adresy zwolniły akurat we wtorki?”).
Trzy filary obserwowalności są dobrze znane: metryki (liczbowe szeregi czasowe, tanie w przechowywaniu, wygodne w agregacji), logi (zdarzenia z kontekstem, drogie w przechowywaniu, ale zawierają szczegóły) i traces (ścieżka pojedynczego żądania przez komponenty). Dla puli proxy obraz jest nieco specyficzny. Między twoim kodem a docelową stroną stoi zewnętrzna infrastruktura, której nie kontrolujesz: adres wyjściowy, operator sieci, trasa do celu. Nie możesz postawić agenta na maszcie komórkowym. Dlatego jedynym punktem, w którym mierzysz wszystko, jest twój własny klient wysyłający żądanie przez proxy. Każde żądanie przez pulę powinno zostawić po sobie dwa ślady: przyrost metryk i wiersz strukturalnego logu. Traces są przydatne, ale dla większości zespołów nadmierne, więc wrócimy do nich tylko w FAQ.
Dlaczego to ważne właśnie dla proxy? Bo degradacja proxy prawie nigdy nie wygląda jak awaria. Serwer nie odpowiada — to proste. Proxy natomiast jest „trochę wolniejsze”, „nieco częściej zwraca błędy połączenia”, „czasem przerywa pobieranie w połowie”. Każdy taki objaw osobno jest poniżej progu uwagi człowieka. W sumie podwajają koszt zbierania danych i psują terminy. Zauważyć powolną degradację można tylko przez pomiar.
Cztery sygnały dla proxy: skuteczność, opóźnienie, ponowne próby, ruch
W inżynierii niezawodności istnieje pojęcie „złotych sygnałów”: opóźnienie, ruch, błędy i nasycenie. Dla puli proxy adaptujemy ten pomysł i otrzymujemy cztery sygnały, których wystarczy na 90% sytuacji. Dlaczego właśnie cztery, a nie dwadzieścia? Bo każdy dodatkowy wykres na dashboardzie zmniejsza szansę, że dyżurny spojrzy na właściwy. Cztery sygnały to tyle, ile mieści się w jednym wierszu ekranu i czyta się w dziesięć sekund.
Sygnał 1: wskaźnik sukcesu
Wskaźnik sukcesu (success rate) to stosunek żądań zakończonych użytecznym wynikiem do całkowitej liczby żądań w oknie czasowym. Kluczowe słowo to „użytecznym”. Sukces dla proxy to nie tylko HTTP 200. Sukces to sytuacja, gdy zasób docelowy odpowiedział tym, czego oczekiwałeś, a odpowiedź dotarła w całości. Przerwanie połączenia w połowie ciała odpowiedzi to nie sukces, nawet jeśli nagłówki miały kod 200. Kod 403 od docelowej strony też nie jest sukcesem, ale to prawdopodobnie nie wina proxy, i rozróżniamy to przez klasyfikację wyniku.
Praktyczna zasada: klasyfikuj wynik każdego żądania do jednej z niewielkiej liczby kategorii. Na przykład: ok (otrzymano oczekiwaną odpowiedź), proxy_connect_error (nie udało się nawiązać połączenia z proxy), proxy_auth_error (proxy odrzuciło autoryzację), upstream_timeout (proxy odpowiedziało, ale zasób docelowy nie odpowiedział na czas), upstream_4xx, upstream_5xx, truncated (połączenie zerwane przed końcem ciała). Ta klasyfikacja to podstawa całej dalszej analizy. Wskaźnik sukcesu liczy się jako ok do całości, ale osobno patrzysz na udziały każdej kategorii błędów, bo wskazują na różnych winowajców.
Sygnał 2: opóźnienie według percentyli
Opóźnienie dla proxy ma sens mierzyć w trzech punktach: czas nawiązania połączenia z proxy, czas do pierwszego bajtu odpowiedzi zasobu docelowego (TTFB, time to first byte) i całkowity czas żądania. Pierwsze mówi o dostępności samego węzła proxy. Drugie — o jakości trasy od proxy do celu i szybkości odpowiedzi celu. Trzecie — o przepustowości kanału. Jeśli rośnie tylko trzecie, a pierwsze dwa są stabilne, problem jest w paśmie, a nie w dostępności. Jeśli rośnie pierwsze, problem jest między tobą a proxy. Opóźnienie trzeba liczyć percentylami, a nie średnią, i temu poświęcony jest osobny rozdział poniżej.
Sygnał 3: wskaźnik ponownych prób
Wskaźnik ponownych prób (retry rate) to najbardziej niedoceniany sygnał. Większość klientów jest napisana tak, że przy błędzie automatycznie powtarza żądanie przez inny adres. To słuszne z punktu widzenia niezawodności i okropne z punktu widzenia obserwowalności: wskaźnik sukcesu pozostaje 99%, a pod maską każde żądanie jest wykonywane trzy razy. Płacisz za ruch potrójnie, zasób docelowy dostaje potrójne obciążenie, a na dashboardzie wszystko jest zielone. Wskaźnik ponownych prób ujawnia ukrytą degradację, zanim stanie się widoczna. Liczy się go jako stosunek liczby prób do liczby żądań logicznych: wartość 1,0 oznacza brak ponownych prób, 1,5 — że co drugie żądanie było powtarzane.
Sygnał 4: zużycie ruchu
Zużycie ruchu to sygnał bezpośrednio związany z pieniędzmi, bo wiele taryf, w tym taryfy Proxeon, rozlicza objętość przesyłanych danych. Ma jednak także rolę diagnostyczną. Gwałtowny wzrost bajtów na jedno udane żądanie oznacza, że zasób docelowy zaczął zwracać inne strony: ciężkie strony-zastępcze, przekierowania do innych sekcji, zmieniony layout. Gwałtowny spadek — że zamiast pełnych stron dostajesz krótkie odpowiedzi o błędzie. Ruch licz w dwóch kierunkach i koniecznie normalizuj na liczbę udanych żądań: „kilobajty na udane żądanie” to znacznie bardziej informacyjna metryka niż „gigabajty na godzinę”.
Jak to wygląda w kodzie
Poniżej minimalny zestaw metryk dla Prometheusa w Pythonie. Zwróć uwagę na zestaw etykiet: pula, kraj, operator, typ proxy. Adres konkretnego proxy celowo nie występuje w etykietach, co wyjaśnimy w rozdziale o wymiarach.
from prometheus_client import Counter, Histogram
LABELS = ["pool", "country", "operator", "proxy_type"]
REQUESTS = Counter("proxy_requests_total", "Żądania logiczne przez pulę", LABELS + ["outcome"])
ATTEMPTS = Counter("proxy_attempts_total", "Próby, w tym ponowne", LABELS + ["attempt_kind"])
TTFB = Histogram("proxy_ttfb_seconds", "Czas do pierwszego bajtu", LABELS, buckets=(0.1, 0.25, 0.5, 1, 2, 4, 8, 16, 30))
CONNECT = Histogram("proxy_connect_seconds", "Nawiązanie połączenia z proxy", LABELS, buckets=(0.05, 0.1, 0.25, 0.5, 1, 2, 5))
BYTES = Counter("proxy_bytes_total", "Bajty przez pulę", LABELS + ["direction"])
def observe(ctx, outcome, ttfb, connect, bytes_in, bytes_out, attempt):
base = dict(pool=ctx.pool, country=ctx.country, operator=ctx.operator, proxy_type=ctx.proxy_type)
kind = "first" if attempt == 1 else "retry"
ATTEMPTS.labels(**base, attempt_kind=kind).inc()
if outcome in ("ok", "upstream_4xx", "upstream_5xx", "truncated", "upstream_timeout"):
TTFB.labels(**base).observe(ttfb)
CONNECT.labels(**base).observe(connect)
BYTES.labels(**base, direction="in").inc(bytes_in)
BYTES.labels(**base, direction="out").inc(bytes_out)
if outcome == "ok" or attempt == ctx.max_attempts:
REQUESTS.labels(**base, outcome=outcome).inc()Zwróć uwagę na szczegół: licznik żądań logicznych rośnie tylko przy sukcesie lub przy wyczerpaniu prób. Dzięki temu wskaźnik sukcesu odzwierciedla wynik dla użytkownika twojego kodu, a licznik prób — rzeczywistą pracę puli. Różnica między nimi to właśnie wskaźnik ponownych prób.
Tabela: metryka, co oznacza jej wzrost, pierwsze działanie
| Metryka | Co oznacza jej wzrost | Pierwsze działanie |
|---|---|---|
| Udział proxy_connect_error | Węzły proxy są niedostępne lub przeciążone; problem między tobą a dostawcą | Sprawdź swój kanał wyjściowy i DNS, potem porównaj po wycinkach: jeśli cała pula — szukaj przyczyny u siebie, jeśli jeden wycinek — powiadom dostawcę z danymi |
| Udział proxy_auth_error | Wygasł dostęp, zmieniły się dane uwierzytelniające, przekroczono limit połączeń | Sprawdź aktualność danych dostępowych i limity w panelu dostawcy, nie ponawiaj takich żądań |
| Udział upstream_timeout | Zasób docelowy odpowiada wolno przez tę trasę albo proxy nie nadąża | Porównaj TTFB po hostach docelowych: jeśli jeden host — problem celu, jeśli wszystkie — trasa proxy |
| Udział upstream_4xx (poza 404) | Zasób docelowy ogranicza intensywność lub zmienił zasady dostępu | Zmniejsz częstotliwość żądań do zasobu, sprawdź zgodność z jego regulaminem, nie zwiększaj ponownych prób |
| Udział upstream_5xx | Problem po stronie zasobu docelowego | Poczekaj, zmniejsz obciążenie; proxy najprawdopodobniej nie ma z tym nic wspólnego |
| Udział truncated | Połączenia są zrywane w połowie: niestabilny kanał, zbyt krótkie limity czasu odczytu | Sprawdź limity czasu odczytu i rozmiary odpowiedzi; porównaj po operatorach |
| p95 czasu połączenia z proxy | Degradacja dostępności węzłów proxy lub twojego stosu sieciowego | Sprawdź liczbę równoczesnych połączeń i wyczerpanie portów u siebie |
| p95 TTFB przy stabilnym czasie połączenia | Trasa od proxy do celu zwolniła albo cel zwolnił | Rozłóż na hosty docelowe i wycinki puli |
| p99 całkowitego czasu przy stabilnym TTFB | Spadła przepustowość kanału proxy lub wzrosły odpowiedzi | Sprawdź bajty na żądanie; jeśli rozmiar się nie zmienił — problem z pasmem |
| Wskaźnik ponownych prób | Ukryta degradacja: sukces utrzymuje się dzięki powtórzeniom, rosną koszty | Znajdź, która kategoria błędów wywołuje powtórzenia, i zajmij się nią |
| Bajty na udane żądanie | Zasób docelowy zwraca inną treść: zastępniki, przekierowania, zmieniona struktura | Sprawdź zawartość odpowiedzi na próbce, zaktualizuj parser i walidację |
| Całkowity ruch przy stabilnej liczbie żądań | Wzrost ponownych prób lub rozmiaru odpowiedzi; bezpośredni cios w budżet | Rozłóż na dwa czynniki: liczba prób i bajty na próbę |
Co zapisywać w logu na każde żądanie — a czego zapisywać nie wolno
Metryki odpowiadają na pytanie „co się dzieje”, logi — na pytanie „z czym dokładnie”. Kiedy dashboard pokazuje wzrost błędów w wycinku „kraj X, operator Y”, musisz zrozumieć, czy winne są wszystkie adresy tego wycinka, czy dwa na trzydzieści. Metryki z etykietą po adresie rozsadzą magazyn, a logi nie, bo logów nie agregujesz stale, tylko filtrujesz na żądanie.
Obowiązkowe pola wiersza logu
Każda próba żądania przez pulę powinna generować jeden strukturalny wpis, najlepiej w formacie JSON. Oto zestaw pól, który uważamy za minimalnie wystarczający:
- ts — znacznik czasu w UTC z milisekundami. Nie czas lokalny. Nigdy.
- request_id — identyfikator żądania logicznego, wspólny dla wszystkich jego prób. Pozwala zebrać całą historię jednego żądania jedną komendą.
- attempt i max_attempts — numer próby i limit. Bez numeru próby nie odróżnisz pierwszego błędu od trzeciego.
- proxy_id — wewnętrzny identyfikator proxy. Nie adres z portem i nie ciąg połączenia. Wewnętrzny identyfikator, który możesz przypisać do prawdziwego adresu przez osobną, zabezpieczoną tabelę.
- proxy_type, country, operator, pool — te same wymiary co w metrykach. To pozwala przeskakiwać między dashboardem a logami bez przeliczania.
- target_host — nazwa domeny celu. Nie pełny URL: w ciągach zapytania bywają tokeny i dane osobowe.
- method — metoda HTTP.
- status — kod HTTP odpowiedzi, jeśli został odebrany, w przeciwnym razie null.
- outcome — ta sama klasyfikacja wyniku, o której mówiliśmy wyżej.
- error_class — klasa wyjątku lub krótki kod błędu transportu, jeśli wynik nie jest ok.
- connect_ms, ttfb_ms, total_ms — trzy opóźnienia w milisekundach.
- bytes_in, bytes_out — rozmiar ciała odpowiedzi i rozmiar żądania.
- session_id — jeśli używasz sesji trwałych, identyfikator sesji; tak zobaczysz, że błędy koncentrują się w długich sesjach.
- retry_reason — dla prób od drugiej wzwyż: wynik poprzedniej próby.
Przykładowy wiersz:
{"ts":"2026-03-14T11:32:07.412Z","request_id":"7f3a9c1e","attempt":2,"max_attempts":3,"retry_reason":"upstream_timeout","proxy_id":"px-2841","proxy_type":"mobile","country":"RU","operator":"op_a","pool":"pricing","target_host":"shop.example","method":"GET","status":200,"outcome":"ok","error_class":null,"connect_ms":184,"ttfb_ms":1420,"total_ms":2310,"bytes_in":48211,"bytes_out":612,"session_id":"s-19b"}Jeden taki wiersz waży około 400 bajtów. Przy 50 żądaniach na sekundę to około 1,7 gigabajta na dobę bez kompresji i 200–300 megabajtów po kompresji. Całkiem znośne nawet dla skromnego budżetu.
Czego w logach być nie powinno
Tu przechodzimy od inżynierii do odpowiedzialności. Lista jest krótka, ale każdy punkt ważny:
- Dane dostępowe do proxy. Najczęstszy wyciek to ciąg połączenia w postaci http://login:hasło@host:port, który trafia do logu w całości, bo programista zalogował konfig „do debugowania”. Logi są kopiowane do ticketów, przesyłane kolegom, trzymane latami. Dane dostępowe w nich to dane dostępowe dostępne wszystkim, kto kiedykolwiek widział log.
- Nagłówek Proxy-Authorization i wszelkie inne nagłówki autoryzacji, w tym nagłówki do zasobu docelowego.
- Ciało odpowiedzi. Jest ogromne, może zawierać dane osobowe osób trzecich i prawie nigdy nie jest potrzebne do diagnozy proxy. Jeśli chcesz zrozumieć, co zwrócił zasób docelowy, zapisz hash ciała i jego długość, a samą odpowiedź — do osobnego magazynu z krótkim czasem życia i tylko na próbce.
- Cookies w obie strony.
- Pełny URL z parametrami zapytania. Wystarczy host i, w razie potrzeby, ścieżka bez parametrów.
- Rzeczywisty adres proxy w otwartej postaci, jeśli log trafia do wspólnego magazynu. Wewnętrzny identyfikator wystarczy.
Praktyka, którą polecamy: zaimplementuj redagowanie na poziomie jednej funkcji, przez którą przechodzi każdy wpis związany z proxy. Nie polegaj na dyscyplinie programistów.
import re
CRED_IN_URL = re.compile(r"(https?|socks5h?)://[^/@:]+:[^/@]+@")
SENSITIVE_HEADERS = {"proxy-authorization", "authorization", "cookie", "set-cookie"}
def redact(record: dict) -> dict:
for key, value in list(record.items()):
if key.lower() in SENSITIVE_HEADERS:
record[key] = "[redacted]"
elif isinstance(value, str):
record[key] = CRED_IN_URL.sub(lambda m: m.group(1) + "://[redacted]@", value)
record.pop("body", None)
return recordSampling i przechowywanie
Czy trzeba zapisywać każdą próbę? Błędy — tak, wszystkie. Udane próby można samplować: przy intensywności powyżej setki żądań na sekundę wystarczy 5–10% udanych wierszy do oceny opóźnień, bo metryki zawierają już pełny obraz. Ważne, żeby decyzja o samplowaniu była podejmowana po request_id, a nie losowo na każdą próbę: inaczej dostaniesz drugą próbę bez pierwszej. Czas przechowywania szczegółowych logów to zwykle 7–14 dni; agregaty i metryki żyją dłużej.
Percentyle zamiast średniej: dlaczego średnia ukrywa problem
Wyobraź sobie pulę stu adresów. Dziewięćdziesiąt odpowiada w 0,8 sekundy. Dziesięć — w 9 sekund, bo trafiły na przeciążoną stację bazową. Średni czas odpowiedzi to 1,62 sekundy. Tydzień temu, gdy wszystko było dobrze, średnia wynosiła 1,4. Wzrost o 15%. Kto na to zareaguje? Nikt. A tymczasem co dziesiąte twoje żądanie czeka dziewięć sekund, i jeśli masz limit czasu na osiem, co dziesiąte żądanie kończy się błędem.
Średnia arytmetyczna dobrze opisuje rozkłady podobne do dzwonu. Opóźnienia przez proxy nigdy nie są podobne do dzwonu. To rozkład z długim prawym ogonem: większość żądań jest szybka, ale niektóre — bardzo wolne, i właśnie one decydują o doświadczeniu użytkownika i kosztach. Co więcej, opóźnienia przez pulę są często bimodalne: jest „normalny” garb i jest „garb problematycznych adresów”. Średnia ląduje między nimi, w punkcie, gdzie realnych żądań prawie nie ma.
Czym są p50, p95, p99
Percentyl to wartość, poniżej której leży zadany odsetek obserwacji. p50 (mediana) — połowa żądań jest szybsza od tej wartości. p95 — 95% szybszych, 5% wolniejszych. p99 — tylko jeden procent wolniejszych. Dla puli proxy polecamy patrzeć na trzy percentyle w różnych celach:
- p50 pokazuje żądanie „typowe”. Jeśli rośnie p50, zdegradowała się większość. To rzadki i poważny sygnał.
- p95 — robocza metryka dla alertów. Jest wrażliwa na problematyczne segmenty (w powyższym przykładzie p95 od razu pokaże 9 sekund), ale odporna na pojedyncze odchylenia.
- p99 — metryka dla limitów czasu i planowania. Twój limit czasu powinien być nieco powyżej „zdrowego” p99, inaczej będziesz przerywać normalne żądania i zamieniać je w ponowne próby.
Jak liczyć dokładnie
Jeśli danych jest niewiele (tysiące wartości w oknie), licz dokładnie: posortuj i weź element po indeksie.
def percentile(values, p):
if not values:
return None
ordered = sorted(values)
k = (len(ordered) - 1) * p / 100.0
lo = int(k)
hi = min(lo + 1, len(ordered) - 1)
return ordered[lo] + (ordered[hi] - ordered[lo]) * (k - lo)
ttfb_window = [0.8, 0.9, 0.7, 9.1, 0.85, 8.7, 0.78, 0.92, 0.81, 0.79]
print(percentile(ttfb_window, 50), percentile(ttfb_window, 95))Dokładne liczenie nie skaluje się: żeby policzyć p95 za tydzień po milionach żądań, trzeba przechowywać wszystkie wartości. Dlatego systemy przemysłowe używają struktur przybliżonych.
Jak liczyć w skali: histogramy
Prometheus i zgodne systemy przechowują histogram jako zestaw liczników po kubełkach: ile obserwacji było poniżej 0,1 s, poniżej 0,25 s, poniżej 0,5 s i tak dalej. Percentyl odtwarza się przez interpolację liniową wewnątrz kubełka. Dokładność zależy od tego, jak trafnie dobrano granice kubełków. Dla opóźnień przez proxy dobrze działa skala logarytmiczna od 100 milisekund do 30 sekund: 0,1; 0,25; 0,5; 1; 2; 4; 8; 16; 30. Jeśli twoje zdrowe p95 to około 2 sekund, a limit czasu 15, taka siatka da błąd w granicach 20–30% na ogonie, co dla alertów i diagnostyki wystarcza.
histogram_quantile(0.95,
sum by (le, country, operator) (
rate(proxy_ttfb_seconds_bucket{pool="pricing"}[5m])
)
)Zwróć uwagę na sum by (le, ...) w środku. To krytyczny szczegół: histogramy różnych instancji twojego klienta trzeba najpierw zsumować, a potem obliczyć kwantyl. Operacja odwrotna — policzyć p95 na każdej instancji i uśrednić — jest matematycznie bezsensowna. Średnia percentyli nie jest percentylem. To jeden z najczęstszych błędów i potrafi ukryć problem równie skutecznie jak zwykła średnia.
Jeśli potrzebujesz wysokiej dokładności na ogonie (np. sprzedajesz SLA na opóźnienie wewnętrznym zamawiającym), użyj struktur typu t-digest lub histogramów HDR, które dają dokładność w ułamkach procenta przy stałej pamięci. Dla większości zespołów standardowe histogramy wystarczą.
Oznaczanie wymiarami: kraj, operator, typ proxy
Oto scenariusz, który ilustruje, po co są wymiary. Masz pulę 400 adresów, 5% z nich to mobilne adresy jednego operatora w jednym regionie. Ten segment zdegradował się całkowicie: wskaźnik sukcesu spadł tam do 10%. Co pokaże ogólny wykres wskaźnika sukcesu dla puli? Spadek z 99% do 94,5%. Zauważalny, ale nie krytyczny. Dyżurny pomyśli „sieć szwankuje” i pójdzie dalej. A zamawiający dane z tego regionu dostanie pustkę.
Oznaczanie wymiarami (w terminologii Prometheusa — etykietami, labels) rozwiązuje dokładnie ten problem: pozwala rozłożyć agregat na wycinki i zobaczyć, że problem jest zlokalizowany. Ale etykiety mają swoją cenę i trzeba ją rozumieć.
Które wymiary wybrać
Dobry wymiar ma dwie cechy: małą, ograniczoną liczbę wartości, a podział po nim odpowiada na realne pytanie diagnostyczne. Dla puli proxy polecamy cztery wymiary w metrykach:
- proxy_type — mobilne, rezydencjalne, data center. Różne typy degradują się różnie i mają różne koszty.
- country — kraj adresu wyjściowego. Zwykle od 1 do 30 wartości.
- operator — operator sieci lub system autonomiczny, przez który wychodzi adres. Problemy często lokalizują się właśnie tutaj: jedna przeciążona stacja bazowa albo jeden zły peering u operatora.
- pool — przeznaczenie logiczne: który produkt lub zadanie korzysta z tego segmentu. Pozwala rozdzielić incydenty według wpływu na biznes.
Piąty wymiar, o którym warto pomyśleć, to target_host, host docelowy. Jest potrzebny, żeby odróżnić problemy proxy od problemów celu. Ale jeśli chodzisz do tysięcy różnych domen, liczność wymknie się spod kontroli. Kompromis: zostaw w etykiecie tylko top-20 hostów według wolumenu, a resztę zlep w wartość other.
Arytmetyka liczności
Każda unikalna kombinacja wartości etykiet to osobny szereg czasowy. Policzmy: 3 typy × 10 krajów × 15 operatorów × 4 pule × 7 wyników = 12 600 szeregów tylko dla licznika żądań. Histogram z 9 kubełkami pomnoży swoją liczbę szeregów przez 10. Razem około 50–60 tysięcy szeregów. Dla Prometheusa to komfort. Teraz wyobraź sobie, że dodałeś etykietę z adresem proxy (400 wartości). Liczba szeregów rośnie 400 razy — do 20 milionów. To już nie „komfort”, to śmierć magazynu.
Stąd zasada: adres konkretnego proxy żyje w logach, a nie w metrykach. Metryki odpowiadają na pytanie „który wycinek się zdegradował”, logi — „które dokładnie adresy wewnątrz wycinka”. Taki podział pracy daje i szybkość, i szczegółowość bez wybuchowego wzrostu kosztów.
Zapytania, które znajdują wycinek
Główne pytanie diagnostyczne brzmi: „gdzie jest najgorzej?”. Odpowiada na nie sortowanie wycinków po wskaźniku sukcesu:
bottomk(5,
sum by (country, operator, proxy_type) (rate(proxy_requests_total{outcome="ok"}[10m]))
/
sum by (country, operator, proxy_type) (rate(proxy_requests_total[10m]))
)Drugie pytanie: „to nowy problem czy tak było zawsze?”. Odpowiada na nie porównanie z przeszłością:
(
sum by (operator) (rate(proxy_requests_total{outcome="ok"}[10m])) / sum by (operator) (rate(proxy_requests_total[10m]))
)
-
(
sum by (operator) (rate(proxy_requests_total{outcome="ok"}[10m] offset 1d)) / sum by (operator) (rate(proxy_requests_total[10m] offset 1d))
)Wartość ujemna pokazuje, o ile wycinek stał się gorszy niż dobę temu. Taki wykres na dashboardzie zamienia „gdzieś coś nie tak” w „operator B w kraju C minus 12 punktów względem wczoraj”.
Skąd biorą się wartości wymiarów
Kraj i typ proxy są zwykle znane z konfiguracji puli. Operatora można uzyskać z metadanych dostawcy: np. w panelu i API Proxeon adresy są wyposażone w informacje o geografii i operatorze, które pobierasz przy inicjalizacji puli i cache'ujesz obok wewnętrznego identyfikatora proxy. Ustalanie operatora w momencie żądania przez zewnętrzne bazy nie jest wskazane: doda opóźnienie i stworzy zbędną zależność.
Alerty, które nie szumią: progi, okna, histereza
Najgorszą rzeczą, jaka może się stać z systemem powiadomień, nie jest pominięty incydent. Najgorsze jest to, gdy dyżurny dostaje 40 powiadomień dziennie, z których 39 nic nie znaczy, i przestaje czytać czterdzieste. Powiadomienie, które się ignoruje, jest gorsze niż brak powiadomienia, bo stwarza iluzję kontroli.
Zasada 1: alert na objaw, nie na przyczynę
Nie stawiaj alertu na „proxy px-2841 zwróciło błąd”. Pojedynczy adres ma prawo chorować; za to odpowiada mechanizm kwarantanny wewnątrz puli, którego tu nie omawiamy. Stawiaj alert na to, co odczuwa konsument twoich danych: wskaźnik sukcesu dla puli poniżej celu, p95 powyżej celu, ponowne próby powyżej celu. Przyczynę znajdziesz na dashboardzie w minutę, jeśli dobrze zrobiłeś wymiary. Zadaniem alertu jest obudzić, a nie wyjaśnić.
Zasada 2: minimalny wolumen ruchu
Wskaźnik sukcesu 50% przy dwóch żądaniach na pięć minut to jedno nieudane żądanie, a nie incydent. Każda reguła dotycząca udziału musi zawierać warunek minimalnej liczby żądań w oknie. Inaczej w nocy, gdy ruch spada, utoniesz w fałszywych alarmach.
Zasada 3: okna i tempo wypalania budżetu
Jeśli określiłeś docelowy poziom niezawodności (np. 98% udanych żądań w miesiącu), masz budżet błędów: 2% żądań. Dobry alert reaguje na tempo wydatkowania budżetu, a nie na wartość chwilową. Praktyka, która stała się standardem w inżynierii niezawodności, to alerty wielookienkowe: krótkie okno (5 minut) łapie gwałtowne spadki, długie (1 godzina) — powolną degradację, i oba muszą być przekroczone jednocześnie, żeby wykluczyć krótkotrwałe skoki.
Zasada 4: histereza
Histereza to różne progi dla wyzwolenia i wyłączenia powiadomienia. Jeśli alert wyzwala się przy wskaźniku sukcesu poniżej 95% i wyłącza przy powrocie powyżej 95%, metryka oscylująca wokół 95% wygeneruje serię powiadomień „wyzwolony — wyłączony — wyzwolony” co kilka minut. Rozwiązanie: wyzwalaj przy 95%, wyłączaj przy 97%. Albo prościej: wymagaj, żeby warunek utrzymywał się nieprzerwanie przez zadany czas (w Prometheusie to słowo kluczowe for), a także ustaw opóźnienie na wyłączenie.
Zasada 5: poziomy istotności i routing
Nie wszystko trzeba dostarczać telefonem. Podziel powiadomienia na dwa-trzy poziomy: krytyczne (wpływ na konsumentów danych tu i teraz — telefon lub push do dyżurnego), ostrzeżenia (degradacja wycinka, wzrost ponownych prób, anomalia ruchu — wiadomość na kanał, do ogarnięcia w godzinach pracy), informacyjne (brak danych od instancji, zbliżanie się do limitu ruchu — dzienny przegląd).
Przykładowy zestaw reguł
groups:
- name: proxy-pool
rules:
- record: pool:success_ratio:5m
expr: sum by (pool) (rate(proxy_requests_total{outcome="ok"}[5m])) / sum by (pool) (rate(proxy_requests_total[5m]))
- record: pool:success_ratio:1h
expr: sum by (pool) (rate(proxy_requests_total{outcome="ok"}[1h])) / sum by (pool) (rate(proxy_requests_total[1h]))
- record: pool:request_rate:5m
expr: sum by (pool) (rate(proxy_requests_total[5m]))
- alert: ProxyPoolSuccessLow
expr: pool:success_ratio:5m < 0.95 and pool:success_ratio:1h < 0.97 and pool:request_rate:5m > 0.5
for: 5m
labels: { severity: critical }
annotations:
summary: "Wskaźnik sukcesu dla puli {{ $labels.pool }} poniżej celu"
- alert: ProxyPoolLatencyHigh
expr: histogram_quantile(0.95, sum by (le, pool) (rate(proxy_ttfb_seconds_bucket[10m]))) > 4 and pool:request_rate:5m > 0.5
for: 10m
labels: { severity: warning }
- alert: ProxyPoolRetryRateHigh
expr: sum by (pool) (rate(proxy_attempts_total[15m])) / sum by (pool) (rate(proxy_requests_total[15m])) > 1.4
for: 15m
labels: { severity: warning }
- alert: ProxyPoolSliceDegraded
expr: (sum by (pool, country, operator) (rate(proxy_requests_total{outcome="ok"}[15m])) / sum by (pool, country, operator) (rate(proxy_requests_total[15m]))) < 0.85 and sum by (pool, country, operator) (rate(proxy_requests_total[15m])) > 0.2
for: 15m
labels: { severity: warning }
- alert: ProxyPoolNoData
expr: absent(proxy_requests_total) or pool:request_rate:5m == 0
for: 10m
labels: { severity: warning }Zwróć uwagę na ostatnią regułę. Brak danych to najpodstępniejsza awaria: klient padł, metryk nie ma, wykresy płaskie i zielone, wszyscy spokojni. Alert na ciszę jest obowiązkowy.
Checklista dla każdego nowego alertu
- Czy w adnotacji jest napisane, co ma zrobić odbiorca? Jeśli nie — reguła nie jest gotowa.
- Czy jest warunek minimalnego ruchu?
- Czy jest opóźnienie for lub histereza?
- Czy reguła została sprawdzona na historii z ostatniego miesiąca: ile razy by się wyzwoliła i ile z tych przypadków było prawdziwymi incydentami?
- Czy jest jasne, do jakiego poziomu istotności należy i gdzie jest routing?
- Czy jest proces zwrotny: jeśli alert się wyzwolił i okazał fałszywy, kto i kiedy go przejrzy?
Szybka diagnoza po dashboardzie: trzy typowe obrazy
Dashboard to nie galeria wykresów, a narzędzie podejmowania decyzji. Dobry dashboard dla puli proxy zbudowany jest jak lej. Górny wiersz: cztery sygnały dla całej puli za ostatnie 6 godzin z nałożonym dniem wczorajszym cienką linią. Drugi wiersz: te same sygnały rozłożone na kraj, operatora i typ — w formie tabeli lub mapy cieplnej, posortowanej po najgorszej wartości. Trzeci wiersz: wskaźnik sukcesu i TTFB dla top-10 hostów docelowych. Czwarty: stosunek czasu połączenia, TTFB i całkowitego czasu. Plus pionowe znaczniki wdrożeń i zmian konfiguracji puli.
Z takim dashboardem diagnoza sprowadza się do odpowiedzi na trzy pytania: „zdegradowała się cała pula czy wycinek?”, „zdegradowały się wszystkie cele czy jeden?”, „które z trzech opóźnień wzrosło?”. Kombinacje odpowiedzi dają trzy typowe obrazy.
Obraz 1: zdegradowały się wszystkie wycinki jednocześnie
Wszystkie kraje, wszyscy operatorzy, wszystkie typy proxy pokazują wzrost błędów lub opóźnień synchronicznie, z dokładnością do minuty. Zewnętrzni dostawcy i operatorzy tak się nie psują: nie mają wspólnego punktu awarii po twojej stronie. Czyli problem jest u ciebie: twój kanał wyjściowy, twój DNS, wyczerpanie deskryptorów plików lub portów lokalnych, niedawne wdrożenie, które zmieniło limity czasu lub rozmiar puli połączeń, konkurencja o procesor z innym procesem na tej samej maszynie. Pierwsze działanie: popatrz na znacznik wdrożenia i na czas połączenia z proxy. Jeśli czas połączenia wzrósł, a TTFB (po odjęciu połączenia) stabilny — problem jest między tobą a węzłami proxy, i prawie na pewno to twoja strona lub twój operator sieciowy.
Obraz 2: zdegradował się jeden wycinek
Jeden operator w jednym kraju albo jeden typ proxy pokazuje spadek, a pozostałe wycinki są stabilne. To najczęstszy obraz. Przyczyna leży u operatora sieci (przeciążenie segmentu, prace serwisowe) albo w segmencie infrastruktury dostawcy proxy. Pierwsze działanie: tymczasowo zmniejsz wagę tego wycinka w rozkładzie obciążenia (mechanizm — poza zakresem tego artykułu) i napisz do wsparcia dostawcy wiadomość z konkretnymi danymi: wycinek, czas początku, metryki przed i po, przykłady request_id. Wsparcie Proxeon reaguje na takie zgłoszenia szybciej niż na „wszystko u was źle”, choćby dlatego, że dostaje gotową lokalizację. Drugie działanie: przejrzyj logi wycinka, żeby zrozumieć, czy zdegradowały się wszystkie adresy w środku, czy kilka.
Obraz 3: spadł wskaźnik sukcesu dla jednego hosta docelowego, opóźnienia w normie
Wszystkie wycinki puli wciąż dobrze chodzą do wszystkich hostów oprócz jednego. Dla tego hosta rośnie udział upstream_4xx lub zmieniła się objętość bajtów na żądanie. Proxy nie są tu niczemu winne. Zasób docelowy zmienił zachowanie: wprowadził ograniczenie częstotliwości, zmienił strukturę strony, zaczął zwracać inną treść dla części żądań. Pierwsze działanie: zmniejsz intensywność żądań do tego zasobu i sprawdź, czy twój zbiór jest zgodny z jego regulaminem. Drugie: sprawdź zawartość na próbce i zaktualizuj walidację odpowiedzi. Kategorycznie nie należy w tej sytuacji zwiększać liczby ponownych prób ani częściej zmieniać adresów — to tylko zwiększy obciążenie zasobu i twoje koszty ruchu.
Obraz 4 (bonus): rosną ponowne próby, sukces się trzyma
Wskaźnik sukcesu 99%, opóźnienia znośne, ale wskaźnik ponownych prób wzrósł z 1,1 do 1,8. To ukryta degradacja. Kosztuje cię już teraz i stanie się widoczną degradacją, gdy tylko wyczerpie się zapas prób. Pierwsze działanie: sprawdź, który wynik stał się przyczyną powtórzeń (pole retry_reason w logach), i dalej idź według jednego z pierwszych trzech obrazów.
Checklista diagnozy w 60 sekund
- Sekundy 0–10: górny wiersz dashboardu. Który z czterech sygnałów odbiegł od wczorajszej linii? Kiedy dokładnie?
- Sekundy 10–25: tabela po wycinkach. Jeden wycinek na czerwono czy wszystkie? Jeśli wszystkie — obraz 1, idź do znaczników wdrożeń i swojej infrastruktury.
- Sekundy 25–40: tabela po hostach docelowych. Jeden host czy wszystkie? Jeśli jeden — obraz 3, proxy nie mają z tym nic wspólnego.
- Sekundy 40–50: stosunek opóźnień. Wzrosło połączenie, TTFB czy całkowity czas?
- Sekundy 50–60: sformułuj jedno zdanie: „od [czas] w wycinku [wycinek] dla [cel] wzrosła [metryka] z [było] do [jest], przypuszczalnie z powodu [hipoteza]”. To zdanie idzie na kanał incydentu i do ticketu u dostawcy.
Typowe błędy, które zerują korzyść z obserwowalności
Widzieliśmy dziesiątki wdrożeń monitoringu wokół proxy. Błędy powtarzają się z zadziwiającą regularnością.
- Średni czas odpowiedzi jako główna metryka. Już omówiliśmy: średnia ukrywa ogon, a ogon to właśnie problem.
- Uśrednianie percentyli między instancjami. Sumuj histogramy, a nie gotowe p95.
- Adres proxy jako etykieta metryki. Wybuch liczności i upadek magazynu po miesiącu.
- Dane dostępowe w logach. Jeden wiersz „for debug” — i hasło leży we wspólnym magazynie logów na rok.
- Ponowne próby liczone jako sukces. Jeśli liczysz tylko wynik końcowy, nigdy nie zobaczysz, że pula pracuje trzy razy intensywniej niż powinna.
- Kod 429 i 403 od zasobu docelowego zapisywany jako błąd proxy. To błąd klasyfikacji, który kieruje dochodzenie w złą stronę i psuje relacje z dostawcą.
- Jedno opóźnienie zamiast trzech. Bez osobnego pomiaru połączenia, TTFB i całkowitego czasu nie odróżnisz problemu dostępności od problemu pasma.
- Alert bez minimalnego ruchu. Nocny szum i utrata zaufania do powiadomień.
- Alert na każdy pojedynczy adres. To zadanie kwarantanny, a nie powiadamiania człowieka.
- Brak alertu na brak danych. Klient padł — wykresy zielone.
- Czas lokalny w logach. Porównywanie z danymi dostawcy i kolegów w innej strefie czasowej zamienia się w torturę.
- Dashboard bez znaczników wdrożeń. Połowa incydentów pokrywa się ze zmianą kodu lub konfiguracji. Bez znacznika będziesz szukać przyczyny na zewnątrz, gdy jest w środku.
- Przechowywanie szczegółowych logów wiecznie. Drogie i bezsensowne: po dwóch tygodniach szczegóły nie są potrzebne, a agregaty są już w metrykach.
Minimalny stos bez przekombinowania
Obserwowalność łatwo zamienić w projekt dla projektu. Zespół trzech osób rozstawia rozproszoną kolejkę komunikatów, klaster bazy kolumnowej i system traces — dla klienta, który robi 20 żądań na sekundę. Nie trzeba tak. Stos powinien odpowiadać skali.
Poziom 1: jedna usługa, do 50 żądań na sekundę
Wystarczą strukturalne logi JSON do pliku lub standardowego wyjścia i jeden skrypt, który raz na minutę agreguje je i zapisuje cztery sygnały do prostej bazy lub nawet tego samego logu. Dashboard — dowolne narzędzie, które umie narysować wykres z tabeli. Alerty — skrypt cron, który porównuje agregaty z progami i wysyła wiadomość do komunikatora. Taki stos stawia się w dzień i pokrywa 80% potrzeb.
Poziom 2: kilka usług, 50–1000 żądań na sekundę
Standardowy zestaw: Prometheus do metryk, Grafana do dashboardów, Alertmanager do routingu powiadomień, Loki lub podobny magazyn do logów z indeksem po etykietach. Klienci eksportują metryki przez wbudowany endpoint HTTP i piszą logi JSON na standardowe wyjście; agent je zbiera. Reguły zapisu (recording rules) z góry liczą wskaźniki sukcesu, żeby dashboardy otwierały się natychmiast. To konfiguracja opisana w przykładach powyżej i pokrywa zdecydowaną większość zespołów.
Poziom 3: dziesiątki usług, tysiące żądań na sekundę
Tu pojawia się sens w OpenTelemetry jako wspólnej warstwie zbierania, w kolumnowym magazynie do logów z możliwością dowolnych zapytań analitycznych po miliardach wierszy i, być może, w traces dla złożonych łańcuchów. Ale nawet na tym poziomie cztery sygnały i struktura logów się nie zmieniają. Zmienia się tylko transport i przechowywanie.
Dane dostawcy jako uzupełnienie
Dostawca proxy widzi swoją stronę: zużycie ruchu na każdym porcie, liczbę aktywnych połączeń, czas ostatniej rotacji. Panel i API Proxeon zwracają te statystyki i warto je podciągnąć obok własnych metryk — choćby do weryfikacji zużycia ruchu: jeśli twój licznik bajtów i licznik dostawcy rozjeżdżają się o więcej niż 10–15%, masz problem z pomiarem (najczęściej nie są uwzględniane nagłówki, uzgadnianie TLS lub zerwane odpowiedzi). Ale dane dostawcy nie zastępują twoich: tylko ty wiesz, które żądanie było udane z punktu widzenia zadania.
Wrapper klienta jako jedyny punkt pomiaru
Niezależnie od poziomu stosu wszystkie pomiary powinny przechodzić przez jeden wrapper nad klientem HTTP. Żadnych „tu zmierzymy, a tu zapomnimy”. Schematycznie:
import time, json, sys, requests
def fetch_via_proxy(ctx, url, timeout=(5, 20)):
t0 = time.perf_counter()
outcome, status, err = "ok", None, None
ttfb = connect = total = 0.0
bytes_in = 0
try:
session = ctx.session # już skonfigurowana na proxy po wewnętrznym proxy_id
resp = session.get(url, timeout=timeout, stream=True)
ttfb = time.perf_counter() - t0
status = resp.status_code
body = resp.content
bytes_in = len(body)
total = time.perf_counter() - t0
if status >= 500: outcome = "upstream_5xx"
elif status >= 400: outcome = "upstream_4xx"
elif not ctx.validate(body): outcome = "invalid_content"
except requests.exceptions.ProxyError as e:
outcome, err = "proxy_connect_error", type(e).__name__
except requests.exceptions.ReadTimeout as e:
outcome, err = "upstream_timeout", type(e).__name__
except requests.exceptions.ChunkedEncodingError as e:
outcome, err = "truncated", type(e).__name__
finally:
total = total or (time.perf_counter() - t0)
observe(ctx, outcome, ttfb, connect, bytes_in, len(url), ctx.attempt)
record = redact({"ts": ctx.now_utc(), "request_id": ctx.request_id, "attempt": ctx.attempt,
"proxy_id": ctx.proxy_id, "proxy_type": ctx.proxy_type, "country": ctx.country,
"operator": ctx.operator, "pool": ctx.pool, "target_host": ctx.host(url),
"status": status, "outcome": outcome, "error_class": err,
"ttfb_ms": int(ttfb * 1000), "total_ms": int(total * 1000), "bytes_in": bytes_in})
sys.stdout.write(json.dumps(record, ensure_ascii=False) + chr(10))
return outcome, statusZwróć uwagę na wynik invalid_content: odpowiedź z kodem 200, która nie przeszła twojej walidacji (np. strona bez oczekiwanego bloku danych), nie jest sukcesem. Właśnie takie odpowiedzi najczęściej maskują zmianę zachowania zasobu docelowego.
Przypadki: jak obserwowalność wygląda w praktyce
Przypadek 1: jeden operator, jeden kraj, minus dwie godziny przestoju tygodniowo
Zespół monitorujący ceny na publicznych platformach handlowych pracował przez pulę około 300 adresów mobilnych i rezydencjalnych przy intensywności około 40 żądań na sekundę. Przed wdrożeniem oznaczania po operatorach widzieli jedno: ogólne p95 TTFB od czasu do czasu skakało z 1,6 do 3–4 sekund na kilka godzin, wskaźnik sukcesu spadał o 3–5 punktów. Dochodzenie w każdym takim epizodzie zajmowało od godziny do dwóch i najczęściej kończyło się słowami „samo przeszło”. Po dodaniu etykiet country i operator pierwszy epizod zlokalizowano w dwie minuty: mobilne adresy jednego operatora w jednym regionie, p95 tam wynosiło 11 sekund przy normie 2. Pozostałe 85% puli było w pełnym porządku. Zespół zmniejszył wagę wycinka w rozkładzie, wysłał dostawcy raport z ramami czasowymi i metrykami, a po kilku godzinach dostał potwierdzenie prac serwisowych u operatora. Wynik za kwartał: czas diagnozy spadł z 90 minut do 5, liczba epizodów się nie zmieniła, ale ich wpływ na ogólne p95 stał się nierozróżnialny.
Przypadek 2: ukryte ponowne próby i +38% na rachunku za ruch
Inny zespół odkrył wzrost zużycia ruchu o 38% w ciągu miesiąca przy niezmienionej liczbie zadań i stabilnym wskaźniku sukcesu 98,5%. Pierwsza hipoteza — strony docelowe stały się cięższe. Metryka bajtów na udane żądanie to obaliła: 46 kilobajtów, jak wcześniej. Za to wskaźnik ponownych prób, na który wcześniej nikt nie patrzył, wzrósł z 1,08 do 1,52. Analiza pola retry_reason w logach pokazała: niemal wszystkie powtórzenia wywoływał wynik upstream_timeout dla jednego hosta docelowego, który zaczął odpowiadać wolniej, ale mieścił się w limicie czasu przy drugiej próbie. Proxy nie były przyczyną w ogóle. Zespół zwiększył limit czasu odczytu dla tego hosta z 8 do 15 sekund (zdrowe p99 wynosiło 11), zmniejszył intensywność żądań do niego i wrócił do poprzedniego poziomu zużycia ruchu w trzy dni. Morał: wskaźnik ponownych prób to metryka o pieniądzach.
Przypadek 3: z 200 powiadomień tygodniowo do sześciu
Trzeci zespół odziedziczył system powiadomień z regułą „wskaźnik sukcesu dla dowolnego wycinka w 1 minutę poniżej 90%” bez ograniczenia ruchu. W nocy, gdy przez niektóre wycinki przechodziło dwa-trzy żądania na minutę, reguła wyzwalała się dziesiątkami. Dyżurni wyłączyli dźwięk. Jeden prawdziwy incydent — spadek dostępności całego segmentu na cztery godziny — został zauważony rano po skargach. Przeprojektowanie reguł zajęło jeden dzień: dodano warunek minimalnego ruchu, wprowadzono dwa okna (5 minut i 1 godzina), dodano opóźnienie for na 10 minut, wycinki przeniesiono na poziom warning, a na poziomie puli zostawiono jeden alert critical. Wynik: około sześciu powiadomień tygodniowo, z których cztery-pięć wymagały realnego działania. Dyżurni znowu włączyli dźwięk.
FAQ: częste pytania o obserwowalność puli proxy
Czy potrzebne są traces, jeśli są metryki i logi?
Dla większości zespołów — nie. Żądanie przez proxy nie przechodzi przez dziesięć mikrousług; ma jedno przejście. Rolę traces pełni tu request_id w logach: po nim zbierasz wszystkie próby jednego żądania. Traces stają się przydatne, gdy klient proxy jest tylko częścią długiego łańcucha (kolejka zadań, parser, walidator, ładowarka do bazy) i musisz zrozumieć, gdzie dokładnie ginie czas.
Jak mierzyć TTFB, jeśli klient HTTP nie zwraca go wprost?
Większość klientów zwraca sterowanie po otrzymaniu nagłówków, jeśli poprosisz o strumieniowe czytanie (stream=True lub odpowiednik). Moment zwrotu to przybliżenie TTFB. Czas połączenia dokładniej mierzy się na poziomie transportu: wiele bibliotek ma haki na zdarzenia połączenia, niektóre — gotową informację o fazach żądania. Jeśli haków nie ma, można mierzyć czas do wysłania pierwszego bajtu żądania osobnym niskopoziomowym pomiarem, ale do celów praktycznych przybliżenie przez strumieniowe czytanie wystarcza.
Jak odróżnić błąd proxy od błędu zasobu docelowego?
Po miejscu powstania. Jeśli połączenie z proxy nie zostało nawiązane albo proxy zwróciło własny kod (np. 407 przy problemach z autoryzacją lub specyficzna odpowiedź bramy), to błąd proxy. Jeśli proxy przyjęło żądanie, a dalej dostałeś kod od serwera docelowego lub przekroczenie czasu oczekiwania na odpowiedź, to błąd między proxy a celem albo samego celu. Klasyfikuj na tej granicy, a potem porównuj po wycinkach: błędy celu widać dla wszystkich wycinków jednocześnie, błędy proxy — dla konkretnych.
Jaki powinien być zdrowy poziom wskaźnika ponownych prób?
Zależy od celów i typu proxy. Dla adresów mobilnych za normę można uznać 1,05–1,15: sieć bezprzewodowa z natury jest mniej przewidywalna. Dla data center — 1,01–1,05. Ważniejsza niż wartość bezwzględna jest jej stabilność: wzrost o 30% względem tygodniowego poziomu bazowego to powód, żeby się przyjrzeć, nawet jeśli wartość bezwzględna wciąż wygląda skromnie.
Jak wybrać progi alertów, jeśli nie ma historii?
Zacznij od obserwacji bez powiadomień: zbierz dwa tygodnie danych, popatrz na rozkład wskaźnika sukcesu i p95 po dniach i godzinach. Próg dla critical ustaw tak, żeby w ciągu tych dwóch tygodni wyzwolił się nie więcej niż raz-dwa razy i za każdym razem zgodziłbyś się, że to incydent. Próg dla warning — żeby wyzwalał się kilka razy w tygodniu. Potem co miesiąc przeglądaj: alert, który ani razu nie doprowadził do działania, albo się zaostrza, albo usuwa.
Czy warto przechowywać ciało odpowiedzi choćby dla błędów?
Dla błędów transportu ciała nie ma. Dla odpowiedzi z nieoczekiwaną treścią (invalid_content) warto zapisać pierwsze kilkaset bajtów lub hash i długość, w osobnym magazynie z czasem życia jeden-dwa dni, z obowiązkowym czyszczeniem z danych osobowych. Pełnych ciał przechowywać nie trzeba: są ciężkie, mogą zawierać dane osób trzecich, a do diagnozy proxy prawie zawsze wystarczy zrozumieć, jaki typ strony przyszedł.
Jak weryfikować własne rozliczanie ruchu z danymi dostawcy?
Licz bajty w obie strony, wraz z nagłówkami, i porównuj sumy dobowe po portach lub sesjach z tym, co pokazuje panel dostawcy. Rozbieżność 5–10% jest normalna: nie uwzględniasz narzutu TLS i zerwanych połączeń. Rozbieżność powyżej 20% to powód, żeby sprawdzić, czy nie liczysz tylko ciała odpowiedzi i czy nie gubisz prób zakończonych wyjątkiem przed odczytem ciała.
Ile czasu zajmuje wdrożenie opisanego podejścia od zera?
Dla jednego klienta z istniejącym wrapperem nad HTTP: dzień na metryki i strukturę logów, dzień na dashboard, dzień na reguły powiadomień i jeszcze dwa tygodnie na obserwację przed włączeniem alertów critical. Jeśli masz kilku klientów w różnych językach, główny czas schodzi na uzgodnienie jednego zestawu wyników i etykiet — tu nie warto się spieszyć, bo zmiana nazwy metryki po roku gromadzenia historii bardzo boli.
Co zrobić, jeśli dostawca nie ma danych o operatorze dla części adresów?
Wprowadź wartość unknown i pilnuj jej udziału. Jeśli unknown to więcej niż 10–15% puli, twoje wycinki po operatorze tracą sens — poproś dostawcę o pełne metadane albo oznacz adresy ręcznie raz przy dodawaniu do puli.
Podsumowanie: jedna minuta zamiast godziny
Wróćmy do sceny z początku. „Proxy zamulają” — zdanie, które nie zawiera informacji. Wszystko, co opisano w tym poradniku, służy jednemu celowi: zamienić je w zdanie z liczbami, wycinkiem, czasem i hipotezą. Cztery sygnały — wskaźnik sukcesu, opóźnienie według percentyli, wskaźnik ponownych prób, zużycie ruchu — dają obraz stanu puli. Strukturalny log z identyfikatorem żądania, identyfikatorem proxy, numerem próby, kodem, trzema opóźnieniami i rozmiarem — szczegóły do dochodzenia. Percentyle zamiast średniej pokazują ogon, w którym żyją prawdziwe problemy. Oznaczanie po kraju, operatorze i typie lokalizuje degradację do wycinka. Alerty z minimalnym ruchem, dwoma oknami i histerezą wyzwalają się rzadko i celnie. Dashboard-lej odpowiada na trzy pytania w minutę.
Od czego zacząć jutro? Pierwszy krok — wrapper nad klientem z jednolitą klasyfikacją wyników i jedną funkcją zapisu. Drugi — histogram TTFB i licznik prób z etykietami po kraju, operatorze i typie. Trzeci — funkcja redagowania logów, przez którą przechodzi wszystko bez wyjątków. Czwarty — dashboard z czterech wykresów i jednej tabeli po wycinkach. Piąty — dwa tygodnie obserwacji i dopiero potem alerty. Po miesiącu spojrzysz na wykres wskaźnika ponownych prób i, całkiem możliwe, zdziwisz się: pula pracowała zauważalnie intensywniej, niż ci się wydawało. A po kwartale zdanie „wszystko nam zamula” zniknie z twojego czatu — nie dlatego, że problemy znikną, ale dlatego, że każdy z nich będziesz nazywać po imieniu.