본문 바로가기
Kafka

Kafka Consumer Retry를 위한 노력

by AsCE_hyunseung 2022. 6. 25.

대충 히스토리 설명

  • consumer에서 이벤트 받아서 처리하는 로직에 써드파티 api를 호출하는 부분이 있는데, 너무 빠르게 호출하는지 자꾸 Throttling Exception 을 뱉는다.
  • 근데 이게 초당 요청을 몇으로 제한하라는 이런 가이드가 없어서, 실패시 retry 하는 방향으로 개선을 해보려고 한다

첫번째 시도 - retryTemplate

  • batch listen(단건으로 말고 레코드 뭉탱이로 들고오는거)을 하고 있어서 retryTemplate 사용 불가능
    • batch records에서 몇번째 record인지 알 수가 없다고 한다.
  • 실패

두번째 시도 - SeekToBatchErrorHandler

  • 위에서 말했듯이 batch listen을 하고 있어서 여기저기 찾아보다가 스택오버플로보고 시도한 방법이다.
    • 최신 버전에서는 deprecated 되었지만 우리가 쓰는 버전에서는 현역이고, 이거 때문에 버전업하는 것 보다는 싸게 먹힐 것 같아서 트라이했다.
  • 테스트 해봤더니 최대 retry 횟수와, backoff 설정한대로 동작을 안했다
    (offset 이동을 안해서 계속 무한 루프를 돌았음, 그래서 수동 커밋 등등 이상한짓 다해봄)
  • 실패

세번째 시도 - RetryingErrorHandler

  • 이 시도부터 공식 문서를 보기 시작해서 적용한 방법이다 (빨리 볼걸그냥.. 정리도 잘 되어있는데)
  • 문서를 보면서 내가 빼먹은 부분이 있는걸 알기 시작했다
    • listener에서 BatchListenerFailedException 을 던져줘야하는 것이었다 (어떤 레코드에서 exception이 터졌는지)
  • listener에서 BatchListenerFailedException 던지게끔하고 다시 트라이 해봤는데, 내가 원하는대로 동작은 했지만 문제가 있었다.
  • batch listen한 records중에서 하나가 에러를 던져서 최대 retry 횟수를 뚫으면 해당 메시지가 속한 records 전체가 유실되는 문제가 있었다.
  • 이게 말로하면 좀 복잡해서 대충 예를 들어보면
    • 1,2,3,4 메시지를 수신했다고 치자, 1번 메시지에서 최대 retry 횟수를 뚫으면 1~4이 전체 유실되는 동작을 했다.
  • 실패

네번째 시도 - RecoveringErrorHandler

  • 위에 핸들러와 어떤 차이점이 있냐면
    • 1,2,3,4 메시지를 수신했다고 치자, 1번 메시지에서 최대 retry 횟수를 뚫으면 1은 유실되고, 나머지 메시지들이 다음 batch records 포함되서 동작했다. (원하는 동작!)
  • 근데 지금 우리가 받는 메시지 payload 특성상 에러터진 로직만 돌리는게 아니라, 전체 로직을 돌려야해서 일단 실패…(?)
    • 특정 exception을 제외하고 retry를 할 수 있게할 수 있었는데 (addNotRetryableExceptions) 그 반대는 없어서 음…

다섯번째 시도 - @Retryable

  • 프레임워크에서 제공해주는 어노테이션이다.
  • exponential backoff + 특정 exception에서 retry를 잘 동작했다 허허
  • 성공
  • 나중에 DLQ 도입할때 RecoveringErrorHandler 요거 적용하기로 했다

참고한 문서

https://docs.spring.io/spring-kafka/docs/2.6.2/reference/html/#annotation-error-handling