유튜브에서 영상을 보던 도중 신묘한 알고리즘이 추천해주는 가상의 시스템을 설계해보는 영상을 본 적이 있다.
이번 글에는 그 내용을 한번 요약 정리해보고자 한다.
보통 서버와 통신을 할 땐 HTTP 통신을 많이 사용한다.
하지만 HTTP 요청은 무조건 클라이언트가 먼저 시작해야 되는 것이 문제.
A가 B에게 메시지를 보내면 그 메시지는 서버에 전달이 되지만 서버에 아무 요청을 하지 않은 B에겐 서버가 먼저 응답을 할 수 없다.
이 문제를 해결하기 위한 몇가지 옵션을 찾아보자.
새로운 내용의 메시지가 있는지 주기적으로 서버에 계속 물어보는 것을 말한다.
메시지 왔니?
아니요
메시지 왔니?
아니요
이번엔 메시지 왔니?
네
이 방식은 request 수가 너무 많아지게 되어 리소스 낭비가 되고, 메시지에 요청 주기만큼 레이턴시가 걸리게 된다.
Polling 방식과 유사한데 단지 서버가 요청을 받고나서 메시지가 없다면 바로 메시지가 없다고 응답을 주는게 아니라 일정 시간만큼의 타임아웃까지 기다린 후 응답을 보내고, 만약 기다리는 도중 메시지가 있다면 바로 응답하는 방식이다.
메시지 왔니?
... 아니요
메시지 왔니?
.. 어? 방금 왔어요!
Polling에 비해 request 수는 줄지만 여전히 많은 request가 가긴 한다. 그리고 레이턴시도 마찬가지로 존재하고.
클라이언트와 서버 사이에 오픈 커넥션을 유지하는 방법이다. 그렇기 때문에 양방향 소통이 가능해진다.
그래서 카카오톡 같은 1:1 채팅에선 웹소켓을 사용한다.
서비스들 간에 데이터를 주고 받는 방법 중에 하나이다.
MSA에서 두 개의 서비스 간에 메시지 큐를 사용하지 않고 이야기하는 방식인 REST API나 RPC를 사용하면 Synchronous 하게 데이터를 주고 받음.
메시지 큐를 사용하면 Asynchronous하게 데이터를 처리할 수 있다.
메시지 큐는 두가지 역할을 통해 작동하는데 하나는 이벤트 발생시 메시지 큐에 전달해주는 Publisher와 이 이벤트가 메시지 큐에 들어왔을 때 알림을 받는 Subscriber이다.
대표적인 서비스로는 Kafka, RabbitMQ가 있다.
Service B가 Topic 1이라는 이벤트에 대해 Subscribe를 하고 있어서 Service A가 Topic 1 이벤트를 메시지 큐로 전달 했을 때, 메시지 큐가 Service B에 구독하고 있는 이벤트가 발생했다고 알려주는 것이다.
여러 장점이 있는데 그 중 대표적인 것이 Decoupling이다.
마이크로 서비스들이 많아지다 보면 서비스 A에 의존적인 서비스가 많을 수 있겠지? 이 서비스 ㅁ에 대한 요청을 REST API/RPC로 Synchronous하게 하려면 모든 디펜던시에 대한 코드를 서비스 A에 넣어야 한다.
이렇게 되면 서비스 A는 점점 복잡해지고 테스트도 어려워질 것이다. =시스템 디자인적으로 굉장히 안좋음
이런 상황에서 메시지 큐를 사용하도록 하면
디펜던시가 확 줄게 된다.
왜냐하면 서비스 A에 대한 의존성을 가진 다른 서비스들이 존재한다느 것을 알 필요가 없어지기 때문이다. 서비스 A의 역할은 메시지 큐에 이미지를 보내주는 것으로 끝인 것이다.(메시지 큐를 도입한 후 서비스 A의 디펜던시는 메시지 큐 1개가 되었다.)
각각의 서비스는 메시지 큐만 알면 됨.
이제 메시지가 전달되는 흐름을 한번 살펴보자.
유저A가 메시지를 보내면 웹소캣으로 chat server 1로 들어간다. 이 메시지는 유저B에게 도착해야 한다. 그러니 유저B의 메시지 큐에 넣어준다. 메시지 큐의 유저B 이벤트를 구독하고 있던 서비스들에게 알림이 간다. DB에도 유저A가 보낸 메시지가 저장이 되고, 유저B가 접속해 있는 chat server 3으로 전달되서 웹소켓으로 유저B에게 전달 되는 것이다.
트래픽 특성
다른 데이터들과 join 할 필요가 거의 없음
RDBMS 같은 경우는 데이터가 많아지고 index가 많아지면 느려짐
Key Value Store의 장점으로는
Key를 만들 땐 Range Scan 하기 쉽게 디자인 해야 한다.
그룹 챗을 몇명까지 지원할 것인가에 따라 디자인이 달라질 수 있음
최대 200명까지라고 가정을 한다면 어느정도의 Fan out은 괜찮다.