Circuit Breaker და retrai
Circuit Breaker და retrai
1) რატომ არის ეს აუცილებელი?
ქსელები არასაიმედოა: ლატენტობა პულსირდება, კვანძები ეცემა, ლიმიტები მიიღწევა. Retrais იხსნის მოკლევადიანი წარუმატებლობებისგან, ხოლო Circuit Breaker იცავს სისტემას კასკადის უკმარისობისგან და „Same-DDoS“. კომბინაცია სწორ ტაიმაუტებთან და ლიმიტებთან ერთად ინარჩუნებს SLO- ს, სტაბილიზაციას უწევს კუდის შეფერხებებს და „გოგონების“ ფასს.
2) ძირითადი პრინციპები
ჯერ ტაიმაუტები, შემდეგ retrai, შემდეგ Circuit Breaker.
ჩვენ მხოლოდ idempotent ოპერაციებს ვატარებთ (GET, უსაფრთხო POST/PUT idempotent გასაღებით).
გამოყავით რეაგირების ბიუჯეტი: საწყისი RPS- ის 10-15% -იანი მარშრუტით.
ლოკალიზებული უკმარისობა: bulkhead (ცალკეული აუზები/კვოტები) + rate-limit.
დეგრადაციის დროს - სწრაფი უკმარისობა, graceful-degradation/stumbs.
3) რეტრაევის სემანტიკა
როდესაც ტირილი
Timeouts შეცდომები: Timeouts, 5xx, ქსელის მიუწვდომლობა, 429 ('Retry-After- ის შემდეგ').
თქვენ არ შეგიძლიათ გადახვიდეთ: აშკარა ბიზნეს შეცდომები (4xx-429), side-effects იდემპოტენტურობის გარეშე (გადახდა გასაღების გარეშე).
სტრატეგიები
Exponential backoff + jitter (სრული ან ერთგვაროვანი): ამცირებს რეაგირების პაკეტებს.
Max attempts: 1-2 (იშვიათად 3) - უფრო საზიანოა.
Budget: გლობალური რეაგირების მრიცხველი/წმ მომსახურებისთვის და per-request „retry tokens“.
Hedging (იშვიათად): მოთხოვნის პარალელური დუბლი t-quantil- ის შემდეგ (p95) - მხოლოდ მკაცრად იდემპოტენტური კითხვებისთვის.
python base = 100 # ms for attempt in range(1, max_attempts+1):
try:
return call()
except Transient as e:
if attempt == max_attempts: raise sleep_ms = min(cap_ms, base 2(attempt-1))
sleep(random(0, sleep_ms)) # full jitter
4) ტაიმაუტები და „სწრაფი უარი“
Client timeout <upstream timeout: იმისათვის, რომ არ დაზოგოთ „ზომბების“ მოთხოვნები.
Делите: connect timeout, read timeout, overall deadline.
Tail-aware Timauts: ჩვენ გავაფართოვებთ p95/p99 + მცირე რეზერვს.
გამოიყენეთ ზოგადი ვადაგადაცილებული ველი (მაგალითად, gRPC 'deadline') და გაატარეთ იგი ჯაჭვში.
5) Circuit Breaker: როგორ მუშაობს
სახელმწიფოები:- Closed: გადის ტრაფიკს, მიიჩნევს შეცდომებს/ლატენტობას.
- ღია: დაუყოვნებლივ აძლევს სწრაფ უარს (ან სარეზერვო პასუხს).
- Half-Open: შემოწმების მოთხოვნები; წარმატების შემთხვევაში - დახურულია.
- შეცდომები/ტაიმაუტები აღემატება X% -იან წილს N მოთხოვნის/წამის ფანჯრის ან p99 ბარიერის ზემოთ.
- შესაბამისი მოცურების სტატისტიკა და მინიმალური მოცულობა (მაგალითად, 50 მოთხოვნა).
6) Bulkhead, კვოტები და „გაყოფა და დაპყრობა“
ნაერთების ცალკეული ტყვიები per-upstream და per-fich.
კვოტები ფრენის მოთხოვნით; ზედმეტი - სწრაფი უარი.
დეფიციტის შემთხვევაში - დაბალი პრიორიტეტის მქონე შეცდომების დეგრადაცია (feature flags).
7) პერიმეტრის ინტეგრაცია (Envoy/Istio/Nginx)
Envoy (retry + outlier + CB, იდეა):yaml routes:
- match: { prefix: "/api" }
route:
cluster: upstream_api timeout: 2s retry_policy:
retry_on: "connect-failure,reset,retriable-4xx,5xx"
num_retries: 2 per_try_timeout: 600ms retry_back_off: { base_interval: 100ms, max_interval: 800ms }
hedge_policy:
hedge_on_per_try_timeout: true initial_requests: 1 additional_request_chance: { numerator: 5, denominator: HUNDRED } # 5%
clusters:
- name: upstream_api circuit_breakers:
thresholds:
- priority: DEFAULT max_connections: 500 max_requests: 1000 max_retries: 200 outlier_detection:
consecutive_5xx: 5 interval: 5s base_ejection_time: 30s max_ejection_percent: 50
Istio (VirtualService fault/retry, მოკლე მაგალითი):
yaml apiVersion: networking. istio. io/v1beta1 kind: VirtualService spec:
hosts: ["payments"]
http:
- route: [{ destination: { host: payments } }]
timeout: 2s retries:
attempts: 2 perTryTimeout: 600ms retryOn: "5xx,connect-failure,refused-stream,reset"
Nginx Ingress (სურათები):
yaml nginx. ingress. kubernetes. io/proxy-connect-timeout: "2"
nginx. ingress. kubernetes. io/proxy-read-timeout: "2"
nginx. ingress. kubernetes. io/proxy-next-upstream: "error timeout http_502 http_503 http_504"
nginx. ingress. kubernetes. io/proxy-next-upstream-tries: "2"
8) ბიბლიოთეკები და კოდი (snippet დასტის)
Java (Resilience4j):java var cb = CircuitBreaker. ofDefaults("psp");
var retry = Retry. of("psp-retry",
RetryConfig. custom()
.maxAttempts(2)
.waitDuration(Duration. ofMillis(200))
.intervalFunction(IntervalFunction. ofExponentialRandomBackoff(100, 2. 0, 0. 5) )//jitter
.retryExceptions(SocketTimeoutException. class, IOException. class)
.build());
Supplier<Response> decorated =
CircuitBreaker. decorateSupplier(cb,
Retry. decorateSupplier(retry, () -> client. call()));
return Try. ofSupplier(decorated)
.recover(BusinessException. class, fallback())
.get();
Go (context deadline + backoff):
go ctx, cancel:= context. WithTimeout(context. Background(), 2time. Second)
defer cancel()
var lastErr error for i:= 0; i < 2; i++ {
reqCtx, stop:= context. WithTimeout(ctx, 600time. Millisecond)
lastErr = call(reqCtx)
stop()
if lastErr == nil { break }
sleep:= time. Duration(rand. Intn(1<<uint(7+i))) time. Millisecond // full jitter time. Sleep(min(sleep, 800time. Millisecond))
}
if lastErr!= nil { return fastFail() }
Node. js (got + p-retry):
js import pRetry from 'p-retry';
await pRetry(() => got(url, { timeout: { connect: 500, request: 2000 } }), {
retries: 2,
factor: 2,
randomize: true,
minTimeout: 100,
maxTimeout: 800,
onFailedAttempt: e => { if (isBusiness(e)) throw e; }
});
9) გამაჯანსაღებელი ბიუჯეტი და SLO
შეიყვანეთ retry tokens: თითოეული retry ხარჯავს ნიშანს; აუზი შეზღუდულია.
დაუკავშირდით error-budget- ს: ბარიერის ზემოთ მდებარე ბარში - გამორთეთ ჭრილობა, უფრო ხშირად გახსენით CB, ჩართეთ დეგრადაცია.
კანარის გამოშვებები: კანარებზე შეამცირეთ მცდელობები და ნიშნები.
10) ჰედინგი (ფრთხილად)
დაიწყეთ დამატებითი მოთხოვნა p95 ვადის გასვლის შემდეგ, გააუქმეთ წაგება.
მხოლოდ კითხვისა და „უსაფრთხო“ იდემპოტენტური ოპერაციებისთვის; შეზღუდეთ წილი (1-5%).
დააკვირდით opstrim- ზე დატვირთვის ზრდას.
11) დაკვირვება
RED მეტრიკები მარშრუტებზე: Rate, Error, Duration (p50/p95/p99).
CB მეტრიკა: სახელმწიფო (Open/half-Open), გახსნის სიხშირე, გამოტოვებული/უარის თქმის მოთხოვნები.
Retrai: attempts/request, retry-rate, დამწვარი ნიშნები.
პერიმეტრი: outlier-ejection, ejection-rate.
ტრეისი: გამოიყენეთ 'retry _ attempt', 'cb _ state', 'hedged = true', შეეცადეთ 'trace _ id'.
12) ინტეგრაცია არქიტექტურასთან
Bulkhead + CB თითოეული კრიტიკული აფსიდისთვის.
რიგები/ასინქრონი: გრძელი ოპერაციებისთვის, გიჟური ტაიმაუტის ნაცვლად.
Kash/stumbs: fail-Open- ის არა კრიტიკული დარტყმებისთვის.
Autoskale: ის არ ანაზღაურებს ცუდ ჭიდაობას - ჯერ შეაჩერეთ „ქარიშხალი“.
13) ანტი შაბლონები
Retrai გარეშე Taimauts - „დაკიდებული“ კონექტორები და ტყვიების ამოწურვა.
Indempotent ოპერაციების განმეორება (ორმაგი ჩამოწერა).
გაუთავებელი ექსპონენციალური ზრდა cap და jitter გარეშე.
ყველა სტრიქონზე ერთი CB არის წარუმატებლობის გადატანა მთელ პროდუქტზე.
უგულებელყოფა 429/' Retry-After '.
კლიენტის ტაიმუტი უფრო გრძელია, ვიდრე აფსიდი (ან საერთოდ არა).
„მკურნალობა“ ბიზნესის შეცდომებით.
14) განხორციელების სიის სია (0-30 დღე)
0-7 დღე
დაადგინეთ მარშრუტები და მათი idempotence.
დაუსვით ტაიმაუტები (კავშირი/read/overall), ჩართეთ მინიმალური ჭრილობები (× 1) და CB ნაგულისხმევი.
გამოყავით აუზები/კვოტები ძირითადი აფსიდებისთვის.
8-20 დღე
ჩართეთ ჯიტერი და გლობალური რეაგირების ბიუჯეტი, რეტრო ალერტები.
პერიმეტრზე outlier-ejection პარამეტრი, დაბალი ფასის სწრაფი უკმარისობა.
დაშბორდები RED + CB/Retry, ეტიკეტები.
21-30 დღე
ჭიდაობის კანარის პროფილები (ნაკლები მცდელობები), თამაშის დღე „ნელი/ნელი აფთიაქი“.
პოლიტიკის დოკუმენტირება: ვინ/რა ახდენს რეაგირებას, შეზღუდვებს, გამონაკლისებს.
გადახედეთ p95/p99 და ტაიმაუტები მონაცემების მიხედვით, და არა თვალებით.
15) სიმწიფის მეტრიკა
მარშრუტების 100% -ს აქვს ტაიმაუტები და Retrais/SV დოკუმენტირებული პოლიტიკა.
Retry-rate ჯდება ბიუჯეტში (10-15%), ინციდენტებში არ არის ადიდებული.
CB მუშაობს სანამ მთელი აუზი ეცემა; კასკადის უარი არ არის.
ტრეისები აჩვენებს მცდელობებს/hedging; p99 სტაბილურია მწვერვალებზე.
კანარის გამოშვებები იყენებენ ჭიდაობის „ფრთხილად“ პროფილს.
16) კონფიგურაციის მოკლე მაგალითები
Resilience4j YAML (Spring Boot, идея):yaml resilience4j:
circuitbreaker:
instances:
psp:
slidingWindowType: COUNT_BASED slidingWindowSize: 100 minimumNumberOfCalls: 50 failureRateThreshold: 50 waitDurationInOpenState: 30s permittedNumberOfCallsInHalfOpenState: 5 retry:
instances:
psp:
maxAttempts: 2 waitDuration: 200ms enableExponentialBackoff: true exponentialBackoffMultiplier: 2. 0 retryExceptions:
- java. net. SocketTimeoutException
- java. io. IOException
Envoy rate-limit (იდეის ფრაგმენტი):
yaml rate_limits:
- actions:
- generic_key: { descriptor_value: "api. payments" }
17) დასკვნა
სტაბილურობა არის დისციპლინა: Taimauts-retrai (ჯიტერი და ბიუჯეტი), Circuit Breaker + bulkhead/კვოტები და სწრაფი უარი. პერიმეტრის პარამეტრები (outlier-ejection), ჩამოკიდეთ dashbords RED/CB/Retry, დააფიქსირეთ idempotenty პოლიტიკა და არ დაივიწყოთ ბიზნეს SLI. შემდეგ მოკლე ჩავარდნები დარჩება შეუმჩნეველი და ნამდვილი ინციდენტები არ გადაიქცევა კასკადის ვარდნად.