[AWS] SQS FIFO: 실시간 거래 시스템 설계

매우 빠른 응답시간과 정확성을 가지는 실시간 거래 시스템을 설계한다고 가정해보겠다.

이런거말이다.

이런 거래 시스템은 대충 이런 기능과 요구사항을 가진다.

  1. 사용자는 들고있는 코인을 원하는 가격에 매도 요청을 할 수 있다.

  2. 사용자는 사기를 원하는 코인을 원하는 가격에 매수 요청을 할 수 있다.

  3. 만약 같은 가격의 매수 요청과 매도 요청이 만난다면, 해당 매수와 매도를 한쌍으로 맞춰서 거래를 체결한다.

  4. 매수/매도 요청은 요청한 시간 순서대로 엄격하게 처리되어야 한다.

  5. 초당 수백-천건까지의 매도-매수 요청이 들어올 수 있다. 그럼에도 빠른 처리 속도를 가져야 한다.



그래서 내가 설계한 구조는 대강 아래와 같다.

매도/매수 요청을 날리면 일단 각 FIFO 큐로 날려서 받은 다음에, 그걸로 Lambda를 호출해서 처리를 하도록 하는 것이다.

SQS FIFO는 말 그대로 선입선출을 지원하는 큐 서비스인데, 따로 관리가 필요하지 않고 비용이 온디맨드라서 꽤 편리한 기능이다. 다만 단점이 있다.
순서를 엄격하게 처리하다보니 TPS에 제한이 좀 있다.
"높은 처리량"을 활성화했을 경우 서울 리전은 1500TPS라서 초당 1500건까지만 받아낼 수 있고, 도쿄나 싱가포르 리전은 3000TPS라서 초당 3000건까지만 버틸 수 있다.
미국에 있는 일부 진골 리전들은 6000TPS까지 지원되기도 한다.

이걸 넘는 정말 초대형 트래픽을 감당해야 한다면, 그때는 키네시스 같은 대규모 큐를 쓰는 편이 좋을 것이다.

이 경우에는 순서가 중요해서 FIFO를 쓰긴 했지만 기본 스탠다드 큐는 이런 제한이 없고 더 저렴하다. 일반적인 상황이라면 스탠다드를 쓰는게 좋다.

그리고 DB는 일반 RDB와 Redis를 혼용했다.
매도/매수에 대한 요청을 다 레디스에 무작정 때려박아서 읽기/쓰기 성능을 최적화하고, 거래가 체결되었을 경우에는 그 유저들의 보유액 관련 데이터를 RDB에 반영시키는 구조다.

RDB 같은 경우는 RDS를 쓴다면 RDS Proxy를 쓰는게 좋다.
그래서 커넥션이 터지지 않기 때문이다.
https://blog.naver.com/sssang97/222576013561




큐 만들기

한번 만들면서 봐보자.

이름 짓고, "높은 처리량" 옵션은 넣어주면 좋겠다.

대충 이렇게 해서 "매수"용 큐와 "매도"용 큐를 하나씩 만들어주면 된다.




Lambda 생성

큐에 뭐가 들어오면 이제 Lambda가 받아서 실질적인 로직들을 수행해주도록 할 것이다.

먼저 Lambda용 보안그룹을 하나 파주고

함수 하나 만들면서

VPC를 할당하고, 방금 만든 보안그룹을 달아준다.

이래야 동일 VPC 내에 있는 리소스들에 자유롭게 접근할 수 있다.

만든 함수의 실행역할에는 "SQS"가 트리거를 걸 수 있도록 "AWSLambdaSQSQueueExecutionRole"을 붙여줘야 하고, 또 읽은 메세지는 지워줘야 하니 SQSFullAccess같은거도 넣어줘야 한다.




큐에 트리거 걸기

한번 어떻게 받아지나 트리거를 붙여보자.

저 탭으로 이동해서


방금 만든 함수를 달아준다.


그럼 이렇게 생길텐데, 이제 저 큐로 메세지를 쏘면 저 함수를 호출하면서 그 메세지가 전달되게 되는 것이다.

한번 쏴보겠다.

쏘면 이제

함수가 호출돼서 로그가 찍히는 것을 볼 수 있겠다.

{
  Records: [
    {
      messageId: '11172afe-543f-4a90-98de-ac221bbab9f5',
      receiptHandle: 'AQEBWXTN+./c9HDpvDv7Ktx6dS+...+.+.../JW3ylny4gdeTS/....+rlML94sWswIKkEJNSlwlbBoOCX2Dm2McSqIGdUETeLy2oKaDYbNcM6leBIEIRRTupi1kxTyJ2ZMNh4Y=',
      body: '{"user_id": 1, "amount": 1000 }',
      attributes: [Object],
      messageAttributes: {},
      md5OfBody: 'f542f4e2910e233be7e075b76ed22871',
      eventSource: 'aws:sqs',
      eventSourceARN: 'arn:aws:sqs:ap-northeast-2:...:bitcoin_buy_dev.fifo',
      awsRegion: 'ap-northeast-2'
    }
  ]
}

이런식으로 event에 들어온다.

그럼 결국 이런식으로 받아서 처리하면 되는 것이다.

코드를 다 적지는 않았다.

redis에 lock을 걸어서 동시성 버그를 잘 잡는 것만 신경쓰면 되겠다.



참조
https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html
https://docs.aws.amazon.com/ko_kr/general/latest/gr/sqs-service.html
https://docs.aws.amazon.com/ko_kr/AWSSimpleQueueService/latest/SQSDeveloperGuide/high-throughput-fifo.html#enable-high-throughput-fifo