🧠 이번 주에 새로 배운 것
- 서킷 브레이커를 단순히 써보기만 했었는데, 어떻게 어떠한 목적으로 쓰는지 고려할 수 있게 되었다.
- 리트라이, 타임아웃을 설정하여 발생할 수 있는 지연, 실패에 대응할 수 있다.
- 설정을 통해 어떤 Exception을 잡을지 관리할 수 있다.
resilience4j.circuitbreaker:
instances:
myService:
failureRateThreshold: 50
slidingWindowSize: 100
recordExceptions:
- java.io.IOException
ignoreExceptions:
- ..
- 우리 시스템의 장애와 외부 시스템의 장애를 분리하여, 외부 시스템 장애시 어떻게 처리할지 고민할 수 있게 되었다.
- 장애만이 문제가 아니라, 지연 또한 우리 서비스가 어떻게 정의하느냐에 따라 장애로 바라볼 수 있다.
- 하나의 서킷 브레이커에 여러 API 요청이 섞이면, 특정 API만 불안정해도 평균 실패율이 낮아져 서킷이 안 열릴 수 있다.
- Resilience4j의 서킷 브레이커, 리트라이 등 여러 개 사용할 때, 우선순위 설정이 필요함을 알게 되었다.
- FeignClient를 통해 API 요청을 수행할 수 있다.
💭 이런 고민이 있었어요
1️⃣ 서킷 브레이커와 리트라이의 다양한 옵션
sliding-window-type
- count_based/time_based → 트래픽을 고려하여 결정
- 피크 타입과 새벽 시간대 트래픽 차이가 크지 않은지 고려하여 결정
slidingWindowSize
- 최근 몇 번/몇 초 동안의 호출을 기준으로 오류율/지연율을 계산할지 결정
- 크게 잡는다면,
- 통계 데이터가 안정적
- 장애 감지가 느려질 수 있다.
- 작게 잡는다면,
- 장애를 빠르게 감지
- 민감하게 반응하여 open/close가 반복
- 장애를 빠르게 격리 → 작은 size
- 시스템이 불안정하다면 → 큰 size
faliureRateThreshold
- 실패 임계치
- 높게 잡으면,
- 서킷이 잘 열리지 않아, 장애 전파
- 민감하게 반응하여 open/close 반복
waitDurationInOpenState
- 서킷이 open 상태로 유지되는 시간
- 더 긴 회복 시간 확보를 원한다면, 차라리 Half-Open 시 허용 호출 수를 줄이는 것이 낫다.
- 빠른 회복 기회를 주되, 무리한 호출을 막음
permittedNumberOfCallsInHalfOpenState
- Half-Open 상태에서 통과시킬 요청의 개수
- 너무 크게 잡으면,
- 사실 상 Closed 상태와 유사
- 너무 작게 잡으면,
- 운 좋게 성공한 몇 건으로 Closed 상태로 돌아갈 수 있다.
- TPS의 10~20%가 적당
2️⃣ Fallback이 정상 응답을 줘야할까?
- PG에 결제 정보를 조회할 때, 조회를 실패한다면 정상 응답으로 임의 정보를 주는게 맞을까?
- 결제 요청의 서킷 브레이커 Fallback은 요청 실패 응답을 줘도 되지만, 결제 정보 조회 시 다른 정보를 제공하면 문제가 될 수 있음
- Fallback을 통해 로그를 찍거나, 예외를 터트려서 롤백시킬 수 있다.
3️⃣ 주문 시, 재고 검증은 어떻게 하는게 좋을까?
- 재고 검증의 방법으로 Redis를 통해 빠르게 할 수 있음 https://medium.com/musinsa-tech/무진장-힘들었지만-무진장-성장한-개발-이야기-e445888579a9
- 그런데 Redis로 INCR/DECR을 하더라도, 결국 재고를 알아야 살 수 있는지/없는지 알기 때문에 갭 발생
- 실제는 큐를 통해 싱글 스레드로 처리
4️⃣ 리트라이는 몇 번 요청하는게 좋을까?
- 몇 번 재시도할지 결정의 근거가 부족
- “이 정도 재시도하면 될까?”라는 생각으로 명확한 기준치가 없다.
- 리트라이를 하는 것과 안하는 것의 차이는 크지만, 1회와 2/3/4 회는 크게 다르지 않음
💡 앞으로 실무에 써먹을 수 있을 것 같은 포인트
- 해당 시스템의 장애를 우리 서비스와 분리시킬 수 있는지 확인하여, 적절한 대응을 고려해보자.
- Fallback을 통해 실패 시, 임의의 응답을 줄 수 있다면 내려주고, 불가능하면 그에 맞는 실패 처리를 하자.
- 커넥션을 불필요하게 오랫동안 점유할 수 있는 상황이 있는지 확인하고, 타임아웃을 통해 이를 관리하자.
🤔 아쉬웠던 점 & 다음 주에 해보고 싶은 것
- 2주차에 진행했던, 설계 문서를 업데이트하자.
- 초기 설계와 이후 구현이 크게 달라지는 상황으로, 설계 문서가 실제 시스템을 나타내고 있지 않다.
- 외부 API 요청을 트랜잭션으로부터 분리할 방법을 고민해보자.
- 현재 주문 API에서 PG 결제 요청까지 이루어지는 상황
- 주문 API에서 PG 결제 요청이 이루어진다면, 트랜잭션을 커밋하고 PG 요청을 보내야 한다.
- 주문 API와 결제 요청 API 자체를 분리하면 근본적으로 해결 가능
- 불필요한 재시도, 서킷 브레이커 카운팅을 없애자.
- 외부 시스템 장애가 아닌, 요청 자체가 잘못되었다면 이는 재시도하여 해결할 수 있는 문제가 아니다.
- 이러한 예외는 제외하여 바로 응답을 내어주어야 한다.
'WIL' 카테고리의 다른 글
| [WIL] 카프카와 이벤트 (0) | 2025.09.07 |
|---|---|
| [WIL] Decoupling with event (1) | 2025.08.31 |
| [WIL] 인덱스와 캐시 (0) | 2025.08.17 |
| [WIL] 동시성 이슈 제어 (4) | 2025.08.08 |
| [WIL] 설계 - Software Design (2) | 2025.07.27 |