TroubleShooting & Study/Architecture & Design Pattern

[RabbitMQ] RabbitMQ를 사용한 분산 서버 간 데이터 동기화

DH_0518 2024. 12. 9. 00:38

현재 우리 회사의 서비스는 Untitled와 Titled 두 개가 존재한다. 이 서비스들에서 통합 계정을 사용하기 위해서는 인증/인가를 담당할 Auth 서버가 필요해서 총 세 개의 분산된 서버를 운용하게 된다.

 

문제는 이 서버들이 사용하는 DB 또한 다르기 때문에, 분산된 서버들에서 공통적으로 사용되는 유저 데이터가 동기화될 필요가 있다는 것이다.

 

이번 글에서는 내가 어떤 식으로 분산 서비스간 데이터 동기화를 구상했는지 알아보겠다

 

 

 

 

 

 

현재 상황 및 방법 선택에 있어서의 기준 정의

 

 

 

먼저, 여러 방법들 중에 우리 서비스에 적용할 방식을 고르기 위해 내가 중요하게 생각했던 기준은 다음과 같다

  1. 문제를 효율적으로 해결할 수 있어야 한다
    : 당연하겠지만, 분산 서비스간 데이터를 실시간으로 동기화 할 수 있어야 한다. 트리거가 발동하면 즉각적으로 각 서버에서 데이터를 동기화 할 수 있어야 한다는 것이다. 또한 어느 정도의 퍼포먼스가 보장되어야 한다

  2. 오버엔지니어링이 되어서는 안되고, 그렇다고 기술 부채가 생겨서도 안된다
    : 현실적으로 서비스 규모를 측정했을 때, 그리고 전송하려는 데이터 종류를 생각해 봤을 때, 최대한 비용이 적어야 하고 추후에 트래픽이 증가하더라도 문제가 없도록 해야 한다. 따라서 학습 및 구현이 복잡하지 않고, 확장에 열려있는 방법을 생각해야 한다

  3. 장애 상황에 대한 대응책이 있어야 한다
    : 서비스를 운영하다 보면 특정 서비스에 장애가 발생할 수 있다. 이때 하나의 서비스가 다운되더라도 다른 서비스에는 영향이 없어야 한다

 

일단 우리 서비스는 추후에 몇 개가 추가될지는 모르겠지만, 현재는 두 개의 작은 서비스를 운용중이다. 그리고 동기화를 위한 데이터들은 회원가입 시에 입력하는 데이터기에, 크기가 작고 자주 입력되는 데이터가 아니다.

 

이제 이런 기준들과 상황에 맞춰서 방법을 선택해보겠다

 

 

 

 

 

 

 

 

1. Redis

 

 

 

처음에는 이미 인증 서버에서 이메일 인증 처리를 위해 Redis를 사용 중이니까 이를 활용할 방법을 생각해 보았고, 회원가입이 이루어지면 회원 데이터를 Redis에 저장하고, 각 서버에서 인증 서버의 Redis에 접근해서 데이터를 빼오는 방법을 생각했다.

 

 

 

이제 이 방법이 기준을 충족하는지 확인해 보자

  1. 문제 해결
    : 분산 서비스 간 실시간 데이터 동기화라는 측면에서는 문제를 충분히 해결할 수 있다. 회원가입이 완료된 데이터를 Redis에 올려놓고, 그 데이터를 각 서버에서 사용하기만 하면 되기 때문이다.
     하지만 이 과정에서 퍼포먼스가 보장이 될지는 의문이다. 먼저 각 서버에서 유저 데이터가 필요한 경우, 매번 Network 통신을 통해 Redis에 접근해야 하는데, 이는 DB Access에 비해 확실히 느릴 것이기에 테스트가 필요하다.
     또한 서버 메모리를 사용하는 Redis에 각 유저의 회원가입 데이터를 전부 넣는다면, 유저 수에 따라 너무 많은 메모리가 낭비될 위험성도 존재한다. 따라서 각 유저 데이터의 크기를 측정한 후, 트래픽이 증가했을 때 어느 정도까지 받아들일 수 있는지를 확인해야 한다
    => 결론: 애매함(3/5)

  2. 오버 엔지니어링, 기술 부채
    : 오버 엔지니어링 측면에서는 Redis는 걱정할 필요가 없다. 이미 인증 서버에서 이메일 인증을 위해 사용 중이라서 Config 파일이나 yml 파일 등 모든 설정 파일이 작성되어 있고, 나도 어느 정도 익숙하게 사용할 수 있기 때문이다.
     하지만 앞서 1번에서 언급했듯이 트래픽 증가에 따른 위험성이 있을 수 있는데, 이를 해결하려면 서버 자체의 스케일업을 진행해야 하기에 고민해봐야 한다
    => 결론: 충분(4/5)

  3. 장애 상황에 대한 대응책
    : 가장 큰 문제점은 장애 상황에 대한 대응책이다. 모든 서비스가 인증 서버에서 사용하는 Redis를 바라보고 있는데, 이 Redis가 다운된다면 각 서비스에서는 유저 데이터가 필요한 기능을 모두 사용할 수 없게 될 것이다.
     Redis-Cluster를 구축해서 하나의 Redis 다운을 막는다고 하더라도 서버 자체가 다운된다면 이는 방법이 없기 때문에 좋은 해결책이 될 수는 없다.
     그리고 2번과 연결되는 문제인데, 만약 클러스터를 구축한다면 현재 서비스 규모를 봤을 때 오버엔지니어링이 될 수도 있다는 생각이 든다
    => 결론: 좋지 못함(1/5)

 

 

 

 

 

 

2. Message Queue (RabbitMQ)

 

 

다음으로, 분산 서비스의 데이터 동기화에서 빠지지 않고 등장하는 메시지 큐이다. 메시지 큐에서 가장 유명하고 많이 언급되는 Kafka는, 대용량 트래픽을 실시간으로 다룰 때 주로 사용되므로 우리 서비스 상황을 봤을 때는 어울리지 않다 판단해서 RabbitMQ를 대안으로 생각하고 비교해 보겠다

RabbitMQ를 사용한 데이터 동기화는, 회원가입 API를 호출했을 때 회원가입 데이터를 각 서버가 consume 하고 있는 queue로 데이터를 보내는 방식을 사용할 것이다. 이를 통해 각 서버의 DB에 유저의 데이터를 저장할 수 있다

 

 

  1. 문제 해결
    : 트리거 API가 호출되었을 때, 데이터를 각 서버에 전송하므로 분산 서비스 간 실시간 데이터 동기화 문제를 해결할 수 있다.
    또한 Redis와는 다르게 각 서버로 전송된 데이터를 DB에 저장 후 필요할 때 꺼내 쓸 수 있으므로 Redis에서 걱정했던 네트워크 통신 문제를 해결할 수 있고, 데이터가 저장되는 위치가 서버 메모리가 아닌 서버의 보조기억장치를 사용하므로 용량 문제도 해결할 수 있다.
    => 결론: 합격(5/5)

  2. 오버 엔지니어링, 기술 부채
    : RabbitMQ는 처음 사용해 보는 기술이기에 따로 공부를 진행해야 하는 비용이 들지만, UI를 통해 쉽게 관리할 수 있고, 간단한 설정만으로 운용할 수 있기에 기술 도입에 큰 비용이 들지는 않다고 생각된다.
     하지만 큐에 쌓인 데이터 하나에 컨슈머 단 한 개만 컨슘 할 수 있으므로, 여러 서버에 데이터를 동시에 전달하기 위해서는 기하급수적으로 큐가 많아질 수가 있어서, 처음부터 설계를 잘해야 한다.
     무엇보다도, 서비스 특성상 유저 데이터 외에는 공유하는 데이터가 없으므로 트래픽이 몰려도 RabbitMQ 만으로 충분히 트래픽을 쳐낼 수 있다 
    => 결론: 충분(4/5)

  3. 장애 상황에 대한 대응책
    : 메시지 큐를 사용하는 데에서 가장 큰 문제점은 분산 서버 특성상 트랜잭션 관리가 어렵다는 것이다. 만약 데이터를 컨슘 한 서버에서 데이터 컨슘 및 저장 과정에 장애가 발생했을 경우, Pub 서버에서부터 트랜잭션이 걸려있으므로 Pub 서버와 Consume 서버 모두 롤백을 시켜줘야 하는 문제가 발생한다. 이는 Saga 패턴 등을 도입해서 해결할 수는 있지만, 각 데이터에 대한 롤백 기능을 구현해줘야 하므로 비용이 많이 든다는 문제가 있다.
     하지만 RabbitMQ에서는 이런 컨슘 과정에서 문제가 생긴 경우, DLQ(Dead-Letter-Queue)로 메시지를 보관할 수 있으므로 메시지 유실에 대한 안정성은 뛰어나다고 말할 수 있다.
    => 결론: 충분(4/5)

 

 

 

 

 

결론

 

 

 

 Redis와 RabbitMQ를 비교해 보았는데, 트래픽과 전송하는 데이터의 크기가 작고, 데이터 동기화 빈도 또한 작기에 최대한 오버엔지니어링이 일어나지 않는 선에서 문제를 해결할 수 있는지를 비교해 보았다.

 결론적으로는 Redis가 RabbitMQ 보다는 더욱 빠르게 서비스에 적용할 수 있지만, 문제 해결 및 장애 상황을 생각해 보았을 때는 RabbitMQ가 더 적절하다고 판단된다.

 따라서 RabbitMQ를 서비스에 적용해 보고, 다음 글에서 SpringBoot에서 RabbitMQ 사용에 대해 알아보겠다!!