마이크로서비스간의 통신 방법에는 여러가지가 있다:

 

여기서는 각 방식에 대한 장단점과 적용할 때 고려할 사항에 대해서 다룬다.

 


 

동기식 블로킹 방식: 요청과 응답

마이크로서비스에서 업스트림 호출자가 다운스트림 수신자에게 요청을 전달한 후 응답이 올때까지 이를 기다리는 방식이다.

 

장점:

  • 간단하며 사용하기 쉽다.

 

단점:

  • 업스트림 호출자와 다운스트림 수신자가 요청 전/중/후에 장애가 발생하는 상황에 대해 생각해봐야한다.
    • 다운스트림 수신자가 장애가 나서 요청을 전달받지 못한 경우에 대해서 어떻게 처리를 할 것인가?
      • Retry, Cancle, Failover, Fallback, Compensating Transaction 등으로 대응할 수 있을 것이다.
    • 둘 중 하나라도 장애가 났을 경우에 데이터가 일시적으로 무결성이 깨지는 상황이 발생할 수도 있을 것인데 이게 용납이 되는가?
      • 물론 DB 에 상태 정보를 기록해두고나서 이후에 상황이 괜찮이지면 다시 데이터의 일관성을 회복하는 방법도 있을 것이다. 여기서는 잠시 일관성이 깨지는 경우에 대해서 허락할 수 없는 비즈니스의 경우도 있을 것이기 때문에 말한 것.
  • 업스트림 호출자는 다운스트림 서비스가 응답을 줄 때까지 기다려야한다.:
    • 다운스트림 서비스가 부하를 많이 받아서 응답에 지연이 있는 경우 업스트림 호출자도 같이 느려질 것이다.

 

적용할 때 고려사항:

  • 마이크로서비스에서 서비스들의 호출이 체이닝식으로 길게 이어지는 경우에는 동기식 블로킹 방식의 단점이 더욱 두드러질 것이다.
    • 불필요한 호출 체이닝이 있을 수도 있다. 이를 백그라운드 작업으로 돌려서 서비스 호출을 없애는 것도 방법일 것.
  • 요청 후 응답이 올때까지 장기간 오래 대기해야하는 경우에는 동기식 블로킹 방식은 적합하지 않다. 리소스를 계속해서 점유하고 있기 때문에.

 

비동기 논블로킹 방식: 요청과 응답

요청과 응답을 사용하는 비동기 논블로킹 방식은 동기식 블로킹 방식의 상위 호환이라고 생각한다.

 

비동기 논블로킹 방식으로 통신을 해도 Callback 을 통해서 연산을 동기식 방식처럼 순서대로 처리해나갈 수 있기 때문이다.

  • 좀 더 자세하게 설명하자면, 업스트림 호출자는 네트워크 통신을 통해서 요청을 보내고 이를 기다리지 않는다.
  • 응답이 오면 지정한 콜백대로 다음 작업을 수행하는 식으로 하면 동기식 방식처럼 순서대로 일을 처리해나갈 수 있다.

 

비동기 논블로킹 방식의 요청과 응답은 동기식 블로킹 방식보다 어렵고 복잡하다는 단점이 있다.

 

하지만 이것만 극복하면 마이크로서비스 통신에서 자주 적용할만한 방식이라고 생각한다.

 

비동기 논블로킹 방식: 공통 데이터 이용

업스트림 서비스에서 자신이 처리한 데이터를 스토로지에 저장해두면 다운스트림 서비스에서 이를 가져와서 처리하는 방식이다.

 

다운스트림에서 데이터를 가져오는 방식은 다음과 같다:

  • Polling 같이 스토로지에 주기적으로 스캔을 통해서 처리할 데이터가 있는지 확인하는 방식.
  • 업스트림 서비스가 스토로지에 데이터를 업로드 한 이후에 다운스트림 서비스에 알려주는 방식

 

적용 케이스:

  • 데이터웨어하우스에서 사용할 수 있는 통신 방법. 데이터 생산자는 웨어하우스에 기록해두면 데이터 소비자는 이를 가지고 가서 처리한다.
    • 즉 한 곳에 저장만해두면 다른 서비스들이 이를 사용하는 형태의 방식.
  • 업스트림 서비스가 배치에 필요한 데이터를 스토로지에 적재해놓고, 다운스트림 서비스를 트리거해서 배치를 실행하는 방식으로도 사용할 수 있겠다.

 

단점:

  • 실시간으로 데이터를 전달하는 방식이 아니라서 처리가 완료되는데 시간이 걸릴 수 있다.
  • 스토로지를 두고 다른 두 마이크로서비스가 결합되어 있는 형태라서 데이터 포맷을 변경하기 어려울 수 있다.

 

장점:

  • Polling 과 같은 방식은 구현이 간단한 편이다.

 

비동기 논블로킹 방식: 이벤트 기반

이벤트 기반의 방식에서 서비스는 연계된 다른 서비스를 몰라도 된다. 그냥 이벤트만 발행시키면 된다.

 

그럼 다운스트림 서비스는 이 이벤트에 반응해서 처리할 것이다.

장점:

  • 업스트림 서비스와 다운스트림 서비스는 서로에 대해서 몰라도 된다. 그래서 느슨한 결합을 할 수 있다.
  • 이벤트는 메시지 브로커에 저장되어서 유실되지 않게 할 수 있다. 이를 처리하는 서비스가 중간에 죽어도 다시 재개가능하다.

 

단점:

  • 이벤트 방식은 요청을 직접 보내는 방식과 달리 추가적인 오버헤드가 있어서 처리가 약간 지연될 수 있다:
    • 이벤트를 발행해야하고, 이벤트는 메시지 브로커에 저장되어야 하며, 이벤트는 소비되어하며, 이벤트를 처리했다는 Ack 를 보내야한다. 그리고 응답을 받을 때도 이벤트 기반으로 받을 것이기 때문에 이와 같은 과정이 한번 더 필요하다.

 

이벤트를 만들 때 어떤 정보를 담아야하는가?

  • Id 와 같은 간단한 정보만 넣는 것:
    • 이 방식은 이벤트 수신자 입장에서 필요한 데이터가 부족할 것이기 때문에 업스트림 서비스를 호출해야하는 문제가 생긴다. 이는 이벤트 기반 방식의 '느슨한 결합' 을 충족시키지 못하기 때문에 추천되지 않음.
  • 이벤트를 처리하는데 필요한 정보를 모두 담는 것: 
    • 이 방식에서 주의해야할 점은 이벤트 메시지 사이즈이다. Apache Kafka 는 데이터 사이즈가 1MB 이상인 것은 전송하지 못한다. 
    • 대부분의 상황에서 1MB 는 충분한 사이즈 일 것이나, 이걸 넘는 경우도 발생할 수 있다. 이런 경우에는 Claim Check Pattern 을 이용하면 된다. (스토로지에 큰 데이터를 적재해놓고, 이벤트 메시지에는 이를 조회할 수 있는 Id 를 제공하는 것) 

+ Recent posts