인프런 커뮤니티 질문&답변

Byung Hyun Choi님의 프로필 이미지
Byung Hyun Choi

작성한 질문수

장애 없는 서비스를 만들기 위한 Resilience4j - CircuitBreaker

n 대의 서버간 서킷 브레이커의 상태를 동기화 시키려면 어떻게 해야 될까요?

해결된 질문

작성

·

366

·

수정됨

1

가상의 상황

  • 외부 연동의 책임을 가진 external-interface 서버

    • 기상청에서 제공하는 api 로부터 오늘의 날씨 를 가져올 수 있습니다.

    • n 대로 스케일 아웃 될 수 있습니다.

    • 현재 구성은 각 서버별로 독립적으로 서킷 브레이커를 관리하고 있습니다.

      • ex)

        • 기상청 api 장애 상황에서

        • 1번 서버로 요청이 집중되어 1번 서버는 open 되었지만

        • 2번 서버는 요청이 별로 들어오지 않아 아직 close 상태일 수 있습니다.

이 때 n 대의 서버간 서킷 브레이커의 상황을 필수적으로 공유해야 된다면 어떻게 할 것 인가?

 

생각하는 안

redis pub/sub 기능을 통해 구현한다.

  1. 1번 서버는 외부 연동 실패로 서킷브레이커가 open 되었다.

  2. 1번 서버는 onStateTransition 의 로직으로 open 상태를 redis 에 pub 한다.

  3. 2번 서버는 sub 하여 자신의 상태를 open 으로 동기화 한다.

하지만 우려되는 맹점이 있습니다.

  • pub, sub 방식은 서버간 동기화의 간극이 있습니다.

    • 결과적 일관성은 보장될지 언정 이 간극은 또 다른 이슈를 유발할 수 있습니다.

    • ex)

      • 1번 서버는 open 상태를 pub 했습니다.

      • 2번 서버는 open 상태를 pub 했습니다. (1번 서버의 open 을 sub 하기 전에)

      • 1번 서버는 close 상태가 되었지만 (외부 연동이 정상적으로 복구되었음에도 불구하고)

        • 2번 서버에서 pub 한 open 정보에 의해 자신을 open 상태로 변경하였습니다.

           

      • 동기화의 간극으로 인해 서로의 open 상태를 공유하는 것이 무한히 반복 됩니다.

  • 물론 실제 실패로의 event 만 pub/sub 하고,

    • redis 의 상태 공유는 pub/sub 하지 않으면 될 것 같기도 합니다.

    • 하지만 가능/불가능 여부를 떠나 여전히 위험할 것 같다는 직감이 듭니다.

  • redisson 과 같은 공유락까지도 생각을 해보았는데, 배보다 배꼽이 큰 것 같다는 생각이 듭니다.

그외

  • 만약 actuator api 호출을 통해 명시적인 변경을 하고 싶다면

    • 실무라면 대표 도메인에 elb 나 ingress 등을 설정할 것 입니다.

    • 그러면 n 대의 서버에 상태 변경 명령을 어떻게하면 api 로 보낼 수 있나요?

    • 혹시 이건 아예 불가능한 생각일까요?


사실 실무라면 굳이 서버간 서킷 브레이커의 상태를 공유하지 않을 것 같습니다.

  • 장애 상황이 자주 발생하지도 않을 것이거니와

    • 서킷 브레이커 발동을 위한 window 크기를 충분히 작게 설정한다면

    • 회복을 방해할 정도의 트래픽이 더 들어가지는 않을 것이라고 생각하기 때문 입니다.

  • 오히려 이런 공유 로직으로 인해 복잡도만 높아질 것이라고 생각해서 입니다.

     

하지만 실무를 떠나 기술적인 방법은 무엇이 있을까 인사이트가 궁금합니다.

답변 1

1

이준형(Foo)님의 프로필 이미지
이준형(Foo)
지식공유자

Byung Hyun Choi 님 안녕하세요!

우선 좋은 질문 남겨주셔서 감사합니다. :)

 

기존에 비슷한 질문이 있었고, 아래 이와 관련해서 답변해드렸던게 있어서 링크 남겨둡니다.

https://www.inflearn.com/questions/1088598/comment/299713

 

그 외에 질문 주신 내용 중 첨언하고 싶은 부분들이 있어서 이야기 드립니다!

 

  1. pub/sub를 통한 동기화에서 발생할 수 있는 동시성 문제

-> 말씀해주신대로 미묘한 타이밍에 서로 open이 반복되면서 문제가 발생할 수 있긴 합니다. 그러나 실제로 발생할 가능성은 매우 낮아보여요. (CircuitBreaker가 열리는 상황이 보통 자주 있지 않고, 애플리케이션이 각각 open으로 빠졌다가 회복되는 시간의 간격이 동시성 문제를 야기할 정도로 짧지 않을 것 같습니다)

물론 종종 발생할 가능성은 분명 있습니다. 그러나 한번 정도 저 문제가 생기고 다시 정상적으로 동작할 가능성이 높지, 무한히 open으로 빠져버릴 가능성은 매우 낮아보입니다.

따라서 여기에 공유락을 사용하는건 불필요하게 시스템 복잡도를 높이는 문제가 생길 것 같습니다.

 

  1. '그러면 n 대의 서버에 상태 변경 명령을 어떻게하면 api 로 보낼 수 있나요?'와 관련된 Reverse Proxy 너머에 있는 호스트들에 대한 API 호출을 통한 동기화 문제

-> 이렇게 호출하는 것도 가능하긴 하지만, 문제가 될 수 있습니다. Reverse Proxy 너머에 있는 호스트들은 로드밸런싱되는 룰이 존재하고, 10대의 서버를 대상으로 10번 요청했을 때 일부 서버는 서킷 상태 변경 요청을 수신하지 못할 가능성이 있기 때문입니다. 물론 더 많은 요청을 보내면 모든 서버를 동기화 시킬 수 있겠지만, 여전히 모든 서버가 동기화되지 못할 가능성은 남아있을겁니다.

따라서 로드밸런싱이 되는 환경에서, 모든 서버를 가급적 동시에 동기화 시키기 위해서는 좋은 방법이 아닙니다.

 

일반적으로 이런 설정을 공유하기 위해서 가장 많이 사용되는 방법은 Spring Cloud, 그중에서도 Eureka를 활용한 동기화 입니다. 이런 툴을 '서비스 디스커버리'라고 이야기합니다. 관련해서는 예제 코드 찾아보시거나, ChatGPT에 '유레카를 통해 여러 애플리케이션의 Resilience4j CircuitBreaker 상태를 공유하는 예제 코드 보여줘' 같은 식으로 질문해보시면 동기화 해주는 예제 코드를 만들어줄겁니다. 한번 확인해보세요. :)

 

다만 이런 Spring Cloud 관련 라이브러리를 사용하고 싶지 않다면, 위에 이야기해주신 것처럼 Redis를 통한 pub/sub 혹은 Kafka를 통한 pub/sub 역시 괜찮은 수단일 것 같습니다. 물론 Redis나 Kafka를 이미 애플리케이션에서 사용하고 있다는 것을 전제로 이야기드리는거고, 사용하지 않고 있는데 오직 Circuit Breaker를 위해 추가하는건 과하다는 생각이 드네요.

 

질문에 대한 답변이 되었을까요?

또 궁금한 내용 있으면 질문 남겨주세요.
감사합니다!

유레카를 이름만 들어보고 써보지는 않았는데, 이번 기회에 최소한 이론이라도 좀 더 봐야될 것 같습니다.
지금 생각나는 질문은 모두 해결된 것 같습니다. 감사합니다.

이준형(Foo)님의 프로필 이미지
이준형(Foo)
지식공유자

넵!! 아 그리고 '사실 실무라면 굳이 서버간 서킷 브레이커의 상태를 공유하지 않을 것 같습니다.' 라고 이야기해주셨는데, 공유가 불필요한 서비스도 있고, 공유를 하는게 꼭 필요한 서비스들도 있어요. ㅎㅎ

 

다만 이걸 선택할 때는 공유가 될때와 안될때 어떤식으로 동작하는지 상상해보시면 좋을 것 같습니다.

 

공유하지 않았을 때는 Client가 호출했을 때 거의 같은 시간인데도 불구하고 어떤 상황에서는 API 호출이 성공(CLOSE or HALF_OPEN)할거고, 어떤 경우에는 실패(OPEN)하겠죠? 이 상황이 애플리케이션에 치명적이라면 반드시 공유하도록 만들어야하고, 치명적이지 않고 이런대로도 괜찮다고 한다면 공유하지 않아도 괜찮습니다.

그 외에도 Circuit Breaker를 통해 보호하고 싶은 리소스(요청을 차단해서 회복되길 기대하는 리소스)를 빠르게 회복시키기 위해서는 전체 요청을 차단시키는게 더 빠르게 회복시킬 수도 있습니다. 이러면 공유하는게 더 괜찮겠죠?

 

아무튼, 다각도로 공유하는게 적절한지 아닌지 판단해보시기 바랍니다.

Byung Hyun Choi님의 프로필 이미지
Byung Hyun Choi

작성한 질문수

질문하기