현재 재직중인 회사의 특성상 환율을 가져와야할 경우가 있습니다.
그런데 이 환율을 제공해주는 외부 사이트 자체에서 특정 시간당 API 요청을 할 수 있는 시간이 제한적이고, 요청 횟 수를 넘어갈 경우 요청을 하지 못하게 되어 있습니다.
이를 해결하기 위해선 돈을 더 주고 높은 Tier의 요금제를 쓰는 방법밖에 없었습니다.
기존에는 각 서버(분리된 여러 서버마다 환율이 필요합니다.) 마다 해당 api를 찌르는 코드들이 스케줄러 형식으로 되어 있었고, 그러다 보니 환율이 추가적으로 늘어나거나 5분내에 동시적으로 3군데 이상에서 요청이 갈 경우 특정 서버는 결과를 받지 못해 문제가 생기는 경우가 생기게 되었습니다.
여기서 우리는 2개의 해결해야할 문제점이 있습니다.
- API 요청 횟수를 최대한 줄여야할 필요가있다.
(1번 요청에 모든 서버가 동일한 환율을 가진다면 최고다)
- 기존에 요청하던 스케줄러를 줄여야할 필요가 있다.
(환율을 가져오는 스케줄러 외에도 많은 스케줄러가 있습니다.)
우선 해당 문제를 해결하기 위해 별도의 환율서버(환율만 가져오며 가져온 환율을 제공)를 분리하는 작업을 진행했습니다.
이렇게 하면, 각 서버에서 요청하던 환율 API를 한곳의 서버(환율서버)로 요청할 수 있으며, 환율서버는 환율을 제공해주는 외부 api에 일정 시간마다 요청하여 해당 정보를 DB에 기록하고, 각 서버들이 요청할 때 해당 데이터를 내려주도록 하는 것입니다.
해당 부분 만으로도 외부 API에 직접적으로 요청하는 횟 수를 줄일 수 있어 나름의 진척이 있다고 볼 수 있지만, 스케줄러 코드도 줄이고, 환율서버에서 알아서 발행만 하면, 각 서버에서 해당 메시지를 읽어 처리하도록 하고 싶었습니다.
이를 해결하기 위해 최종적으로 AWS의 SNS 와 SQS를 활용하여 FAN-OUT을 통한 메시지 처리 방식을 사용했습니다.
그렇다면 AWS SNS와 SQS는 무엇일까요?
우선 SNS는 애플리케이션에서 발행되는 푸시 메커니즘을 통해 시간이 중요한 메시지를 여러 구독자에게 보낼 수 있습니다.
SQS는 분산 애플리케이션이 폴링 코델을 통해 메시지를 교환하는데 사용하는 대기열입니다.
최종적으로 SQS 대기열이 SNS의 특정 주제를 구독하면, SNS에 메시지가 푸시 될 경우 구독 하고있는 각 SQS에 메시지를 전송할 수있는 구조입니다.
그럼 여기서 말하는 FAN-OUT 방식이란 무엇이며, FAN-OUT은 뭘까요?
실제 FAN IN, FAN OUT은 디지털 논리회로에서 논리 게이트에 연결될 수 있는 입력과 출력의 최대값을 의미합니다.
FAN-IN : 게이트에 연결될 수 있는 최대입력수.
FAN-OUT : 게이트의 출력에 연결될 수 있는 입력게이트의 최대 수.
이를 소프트웨어 아키텍처에서도 비유적으로 사용하며, 이를 소프트웨어 에선 메시지를 하나 이상의 대상에 병렬로 전달 하며, 메시징을 실행하는 프로세스를 중단하지 않는 저보 교환을 모델링 하는데 사용되는 메시징 패턴의 의미로 사용됩니다.
흔이 메세지 패턴에서 사용하는 방식으로 발행자가 특정 주제로 메시지를 발행하고, 해당 주제에 관심을 가진 구독자는 해당 메시지를 수신하는 구조입니다.
아래는 제가 사용했던 SNS - SQS 의 FAN-OUT 방식입니다.
하나의 공통된 주제를 발행(SNS)하면, 이를 구독하고 있는 SQS에서 읽어들이는 구조 방식 입니다.
이러한 대표적인 PUB/SUB, FAN-OUT의 사용 사례로는 흔히 말하는 Redis의 PUB/SUB, Kafka, RabbitMQ, AWS의 SNS, SQS등이 있습니다. 이중 저는 사내에서 제공하고 있는 AWS 계정을 활용하여 SNS, SQS를 선택했으나, 각 상황에 따라 어떠한 플랫폼을 사용할지는 고민해 봐야 합니다.
그럼 이러한 FAN-OUT 방식을 제일 잘 활용하고, 대표적으로 볼 수 있는 곳은 어딜까요? 바로 인기 있는 소셜 미디어 플랫폼인 Twitter에서 잘 활용하고 있습니다.
트위터는 2012~2013년 기하급수적으로 성장하기 시작했고, 초당 약 600개의 트윗을 처리하게 되었습니다.
물론 쓰기에서는 큰 문제가 없이 관리가 가능했지만, 실제 문제는 트윗을 읽는 모든 사용자가 Twitter 홈페이지를 쿼리하고, 그들이 팔로우하는
모든 사람의 트윗을 제공 받아야 하는 문제가 있었으며, 이는 초당 600K 즉 60만에 육박합니다.트위터는 이를 해결하기 위해 사용자 타임라인과 홈 타임라인 두개의 타임라인을 관리합니다.
사용자 타임라인은 모든 트윗의 모음이며, 홈 타임라인은 사용자가 팔로우하는 모든 페이지의 트윗에 대한 모음입니다.
Twitter는 어떤방식으로 트윗이 작성되고, 어떻게 사용자에게 보여지게 될까요?
기본적인 트위터의 방식은 유저가 글을 쓰면(트윗을 하면) 해당 유저를 구독하고 있는(팔로우하고 있는) 유저들의 피드창에, 전달되며, 이는 팔로워가 로그아웃 상태여도 적시에 전달되며 팔로우 하는 사람의 모든 트윗을 가져오는 구조입니다.
이때 Redis 의 클러스터를 사용합니다.
트위터는 처음 모든 사용자의 홈 타임라인을 계산하여 Redis 클러스터에 저장하고, 사용자가 홈페이지를 방문할 경우 읽기 요청이 진행됩니다. 이때 트위터는 클러스터중 하나에서 캐시된 사용자의 홈 타임라인을 찾고 이를 사용자에게 그대로 보여줍니다.
위의 그림과 같이 사용자가 쓰기 요청인 트윗을 보내면 해당 트윗이 사용자 팔로워의 모든 타임라인에 복제되어 각 팔로워의 홈 타임라인 대기열에 삽입 됩니다.
즉 저를 팔로우 하는 사람이 1000명이라면, 저는 1000개의 쓰기권한으로 저를 팔로우하는 각 사용자의 홈 타임라인 Redis 클러스터에 삽입을 하는 것이죠.
이는 데이터베이스 디스크에 직접 닿지 않고도 수백만 명의 사용자에게 즉시 서비스를 제공할 수 있으므로 지연시간을 획기적으로 줄일 수 있으며, 이는
풀과 반대되는 개념으로 이를 FAN-OUT이라고 합니다.
그런데 이러한 홈타임라인에 내가 만일 유명인을 팔로우 하고있다면, 가령 대표적으로 현시점 기준 팔로워 1등인 일론 머스크를 팔로우하고 있다면, 어떻게 될까요?
일론 머스크가 글을 쓰기 시작후 트윗을 등록하면, 그를 팔로우 하고 있는 모든 사람 약 1억 4070만명에게 FAN-OUT이 발생합니다. 짧은순간에 약 1억 4070만명에게 트윗을 전파해야 하는 어마어마한 양입니다.
이는 몇가지의 문제점이 있는데, 그 중 가장 큰문제가 바로 아직 누군가의(일론 머스크를 팔로우하고있는) 타임라인엔 일론머스크의 트윗이 업데이트 되지 않은 상태에서 몇 분이 지난 후에 답변을 달 수 있는 상황이 생기거나,
다른 누군가 일론머스크의 타임라인을 보고 답변을 달 시에, 팬아웃에 의해 아직 일론머스크의 원래 트윗을 받기도 전에 누군가 답변을 달거나, 리트윗한 내용을 먼저보게 됩니다.
이를 방지하기 위해 트위터는 팔로워가 너무 많은 사람이 트윗을 할 경우
팬아웃이 발생하지 않는 하이브리드 접근 방식을 고안했습니다.
크게 기대했다면 미안하지만, 생각보다 엄청 고차원적인 해결방법은 아닙니다.
우선 일론 머스크 같은 많은 팔로워를 보유한 사람이 글을 쓸 경우 바로 그를 팔로우한 모든 유저의 타임라인에 병합하는 것이 아니라, 각 사용자중 홈페이지 새로고침을 요청한 사용자에 한해서 타임라인이 병합됩니다.
좀 더 자세하게 알아보자면, 일반 사용자가 새로고침을 요청할 경우 해당 사용자가 팔로우 하고 있는 유명인 트윗이 있는지 확인합니다.
있다면, 해당 유명인의 '유저 타임라인'으로 이동하여 최근에 작성된 트윗이 있는지 확인하고, 있다면 해당 트윗을 가져와 사용자의 캐시된 '홈 타임라인' 트윗 리스트 응답에 합쳐주는 것입니다. 이 작업을 반복적으로 하는 것이죠(유명인을 여럿 팔로우 하고 있다면)
어떻게 보면 일반 사용자가 글을 쓸 때보다 더 많이 생략된 작업으로 유명인의 트윗을 해결하는 방식으로 수정하여, 대부분의 문제가 해결되었다고 합니다.
본론으로 돌아와 제가 진행한 프로젝트인 환율서버는 인플루어선서가 아닙니다. 말 그대로 약 5개의 서버에게 동일한 데이터를 동일한 타임에 보내는 것이 목표이고, FAN-OUT이 발생될때 많은 서버에 까지 전달할 필요가 없습니다.
따라서 만일 해당 환율서버에 약 10만대의 서버가 동시에 요청을 한다면, 즉 제가 만든 서버가 외부 환율 API 자체가 된다면, 그때는 FAN-OUT 방식 보다는 요청을 받고 읃답을 내려주는 형태가 더 맞는 형태일 것입니다.
글을 정리하면서 트위터의 FAN-OUT 방식을 보면서 우선 일반 사용자여도 어마어마한 양의 트윗을 텐데 이를 견디는 Redis Cluster환경을 가지고 있다는 것과, 이를 Redis를 활용한 FAN-OUT과 캐시를 활용하여 피드 리퀘스트를 상당히 간단하게 처리하고 있다는 생각이 들었습니다.
안녕하세요. 글 잘 읽었습니다!!
환율 제공해주는 곳에 환율 변경시 구축하신 aws sns로 발행해달라고 요청하신걸까요?