Microservices 통신

안녕하세요! 6팀의 PM 김지운입니다. 마이크로 서비스 아키텍처를 설계하는 것에 있어 서비스 간 통신으로 어떤 방법을 채택할 것인지 선택의 순간이 찾아오기 마련입니다. 이번 포스팅에서는 저희팀이 어떤 기준으로 어떻게 마이크로 서비스끼리 통신 시킬 것인지 결정하게 되었는지 말씀 드리려 합니다.

동기 VS 비동기

마이크로서비스가 서로 통신하는 한가지 방법은 HTTP를 통한 것이고, 이 통신은 동기식입니다. 요청을 보낼 때 발신하는 마이크로서비스는 응답을 받을 때까지 기다려야 합니다.

그리고 이는 1대1 통신 방식입니다.

또 다른 통신 방법은 비동기식으로, AMQP(Advanced Message Queuing Protocol) 같은 프로토콜을 사용할 때입니다. 마이크로 서비스가 큐를 사용하여 메시지를 교환하고, 메시지는 큐에 넣어지며 발신 마이크로서비스는 메시지가 처리될 때까지 기다리지 않습니다.

동기 방법 (OpenFeignClient)

OpenFeignClient를 이해하기 위해선 Neflix Eureka(Eureka Discovery Service)에 대해 이해할 필요가 있습니다. 하지만, 이번 포스팅에서는 자세히 다루지 않고 별도의 게시글을 작성해 첨부 하도록 하겠습니다.
마이크로서비스는 Eureka에 등록되고, Eureka는 모든 마이크로서비스의 주소와 포트 번호를 갖고 있게 됩니다.

따라서, 마이크로서비스는 Eureka에 등록된 모든 마이크로서비스의 위치를 알 수 있게 되는 것입니다.

HTTP 클라이언트인 OpenFeignClient는 원격 또는 내부 마이크로서비스에 http 요청을 보내고 응답을 받는 데 도와줍니다. HTTP 통신을 위해 간단히 Rest template을 사용하는 경우도 있는데, 가장 큰 차이는 Feign이 선언적 웹 서비스 클라이언트라는 것일겁니다.

이외에도 다음과 같은 기능들을 내장하고 있습니다.

  • 로드 밸런싱 : Feign은 Ribbon을 내장하고 있어 클라이언트 측 로드 밸런싱을 자동으로 지원합니다.
  • 로깅 및 모니터링 : Feign은 선언적인 예외 처리를 지원하여 서비스 간 통신에서 발생하는 문제를 보다 쉽게 다룰 수 있습니다.
  • 예외 처리 : Feign은 선언적인 예외 처리를 지원하여 서비스 간 통신에서 발생하는 문제를 보다 쉽게 다룰 수 있습니다.

동기 통신을 선택하는 경우엔 요청과 응답이 거의 동시에 이루어져야 하는 경우에는 동기 방식이 더 적합합니다. 특히, 트랜잭션처리를 위해선 동기 통신이 필요합니다. 또, 높은 수준의 데이터 일관성이 필요할 때 동기 통신을 고려할 수 있습니다.

비동기 방법 (kafka)

비동기식 통신 방법으로 앞서 AMQP(Advanced Message Queuing Protocol)을 설명 드렸습니다.
메세지 큐를 제공하는 오픈 소스들은 다양한데요. RabbitMQ, AmazonMQ, Kafka 등이 있습니다.

이전에 이러한 내용으로 “kafka를 활용한 Event Driven Microservice” 라는 주제로 3차 기술세미나를 발표했습니다. 영상은 아래에 첨부하도록 하겠습니다.

10분이라는 짧은 시간이라 모든 내용을 설명하기는 어려웠는데요. 마이크로서비스끼리 통신을 위해 kafka를 어떻게 활용하는지에 대한 내용이었습니다.

비동기 통신을 선택하는 경우엔 요청과 응답이 즉시 이루어질 필요가 없고, 요청을 처리하는 데 시간이 오래 걸릴땐, 비동기 방식이 유리합니다. 영상에서도 설명했던 것처럼 여러 서비스가 복잡한 작업 흐름을 가질 때에도, 각각의 작업을 분리하여 비동기적으로 처리할 수 있습니다.

이외에 장점을 정리해서 말씀드리면,

  • 확장성 : 시스템이 확장될 가능성이 있다면, 비동기 통신이 더 나은 선택지가 될 것입니다.
  • 결합 격리 : 한 서비스가 실패했을 때 그 영향을 최소화하고 시스템 전체의 복원력을 높이려면 비동기 통신을 고려해볼 수 있습니다.
  • 프로덕션 레디 : 대용량 트래픽과 높은 병렬 처리가 요구되는 프로덕션 환경에서는 메시지 큐 등을 활용한 비동기 통신이 더 적합할 수 있습니다.

선택의 순간

마이크로서비스끼리 통신 방법을 선택하는 것에 있어서 아키텍처가 비동기 방식의 통신과 더 궁합이 좋아보이기 때문에 Event Driven Design과 Apacha Kafka라고 하는 기술을 사용하는 것이 속도 측면에서 좋아보이는데요. 저희 팀 역시도 마이크로서비스끼리의 통신을 위해 Kafka를 도입하고자 하였습니다. 하지만, “굳이?”라는 질문에 답을 내려야 하는 순간에 부딪혔는데요. 다음과 같은 질문들을 우리 스스로에게 해야만 했습니다.

  1. 카프카를 어떤 목적으로 쓰려는건데?
  2. 트랜잭션 처리는 어떻게 할건데?

위 세가지 질문을 통해 찾은 답은 다음과 같습니다.

Why?
카프카를 로그 저장소와 메세지 큐로, 사용자 요청에 대한 손실을 방지하고, 대기열 순서로 처리함으로써 트래픽 부하로 인한 장애를 방지하기 위해 사용할 것입니다.

How?
무작정 사용하기 XX 동기 방식과 비동기 방식의 목적을 이해하고 필요한 곳에 사용하기

구현하기

저희 프로젝트의 아키텍처를 MSA 도전기 2화에서 보셨을텐데요. 그 사이 최종적으로 아키텍처가 변경이 되었습니다.

Architect

보시는 것처럼. 특별히 응답이 필요하지 않은 상황에서는 메세지 큐 방식으로 카프카를 사용하였고, 이벤트를 발행하기 위해 다른 서비스의 데이터베이스에 접근해야 하는 경우엔 @FeignClients를 사용했습니다.

저희의 아키텍처의 특징이라 한다면, 마이크로 서비스간 통신이 복잡해지지 않게 최대한 단순하게 가져가려 하고 있습니다. 따라서, 마이크로 서비스간 통신에 있어 카프카만 사용하는 것이 아니라 동기 방식도 동시에 사용하면서 구현의 난이도를 낮추고자 하였습니다.