LabHub

마이크로서비스 아키텍처 · 동기 통신(REST·gRPC)과 그 대가 · 실습

서비스 간 REST 호출에 타임아웃과 재시도 넣기

LabHub 에서 이어서 보기

목표

불안정한 다운스트림을 상대로 타임아웃, 지수 백오프, 지터, 재시도 금지 조건, 폴백을 직접 구현해 동기 호출의 기본기를 완성한다.

왜 중요한가

동기 호출에서 사고는 대개 "타임아웃이 없어서" 또는 "재시도를 잘못해서" 납니다. 타임아웃이 없으면 다운스트림이 느려질 때 업스트림의 커넥션 풀이 먼저 마르고, 다운스트림과 무관한 요청까지 실패합니다. 반대로 재시도를 고정 간격으로 무제한 걸면 다운스트림이 회복하려는 순간마다 같은 파도가 덮칩니다. 실제 사례에서 주문 인스턴스 20대가 초당 100건을 처리하며 maxAttempts 5 로 재시도하자 결제 서비스가 초당 10,000건을 받았습니다. 재시도는 부하를 더하는 것이 아니라 곱합니다. 그래서 이 실습은 값을 넣는 순서를 중요하게 다룹니다 — 먼저 타임아웃, 그다음 재시도 조건, 그다음 백오프와 지터, 마지막에 폴백입니다.

단계

1. /opt/app/flaky.py 를 127.0.0.1:8110 에 띄운다. GET /fast 가 200 이어야 한다.
2. /root/rest/call.py 를 만들어 /fast 를 호출하고 응답 본문을 /root/rest/fast.out 에 저장한다.
3. /root/rest/timeout.py 를 만들어 /slow 를 1.0초 타임아웃으로 호출한다. /root/rest/timeout.out 첫 줄에 TIMEOUT, 둘째 줄에 elapsed=<초> 를 적는다. 경과 시간은 2.0초 미만이어야 한다.
4. /root/rest/retry.py 를 만들어 /flaky?key=lab&fail=2 를 지수 백오프로 재시도해 최종 성공시킨다. /root/rest/retry.logattempt=<n> wait=<초> 형식으로 시도마다 한 줄씩 남기고 총 3줄이 되게 한다.
5. retry.py 의 대기 계산을 full jitter 로 바꾼다. 시도 번호 2의 대기 시간을 20번 뽑아 /root/rest/jitter.txt 에 한 줄씩 적는다. 서로 다른 값이 15개 이상이어야 한다.
6. /root/rest/retry400.py/bad 를 호출한다. 400 은 재시도하지 않으므로 /root/rest/retry400.log 는 정확히 1줄이어야 한다.
7. /root/rest/budget.txtinstances=20, rps=100, max_attempts=5, worst_rps=10000 네 줄을 적는다.
8. /root/rest/gateway.py 를 127.0.0.1:8111 에 띄운다. GET /order 는 다운스트림 /always500 을 호출하되, 타임아웃과 재시도를 적용하고 최종 실패 시 200 과 {"degraded":true} 를 준다.

참고

단계 8개

  1. 불안정한 다운스트림 띄우기
  2. 정상 호출로 기준선 잡기
  3. 느린 응답을 타임아웃으로 끊기
  4. 지수 백오프 재시도 구현하기
  5. 지터로 재시도 시점 흩뜨리기
  6. 확정적 오류는 재시도하지 않기
  7. 재시도 예산 계산하기
  8. 폴백이 있는 게이트웨이로 종합하기