๋๋ ํธ๋ํฝ ์ฒ๋ฆฌ๋ฅผ ์ํ Virtual Waiting Room
- #virtual waiting room
- #vwr
Virtual Waiting Room
์๋ ํ์ธ์. ์๋์์์ ๋ฐฑ์๋ ์๋น์ค๋ฅผ ๊ฐ๋ฐํ๊ณ ์๋ ์ ๊ทผ์๋ผ๊ณ ํฉ๋๋ค. ์ด๋ฒ ๋ธ๋ก๊ทธ์ ์ฃผ์ ๋ Virtual Waiting Room(์ดํ VWR) ์ ๋๋ค.
์๋์์์๋ ์ธ๊ธฐ ์ํฐ์คํธ์ ๊ตฟ์ฆ ํ๋งค, ํ์คํฐ๋ฒ ํฐ์ผ ํ๋งค ๋ฑ ์ฌ์ฉ์๋ค์ด ํน์ ์๊ฐ์ ๋ชฐ๋ฆฌ๋ ์ด๋ฒคํธ๋ค์ด ์์ฃผ ๋ฐ์ํฉ๋๋ค. ๊ทธ๋์ ์๊ฐ ๊ธ์ฆํ ์์ฒญ๋ค์ ์ ์ ๋๊ธฐ์ํค๋ ๋๊ธฐ์ด ์์คํ ์ด ํ์ํด์ก์ต๋๋ค.
์ฌํ ๋ค๋ฅธ ์๋น์ค์์๋ ํํ๊ฒ ์ ํ ์ ์๋, ์ฌ์ฉ์์๊ฒ ๋ช๋ช ์ด๋ ๋๊ธฐ์ค์ธ์ง, ์๋น์ค๋๊ธฐ๊น์ง ์ผ๋ง๋ ๊ฑธ๋ฆด์ง ๋ฑ์ ๋ณด์ฌ์ฃผ๊ธฐ ์ํ ์์คํ ์ ๋๋ค.
๊ธฐ์กด์ ํด๋น ๊ธฐ๋ฅ์ ์ ๊ณตํด์ฃผ๋ ์ ๋ฃ ์๋น์ค๋ฅผ ์ฌ์ฉํ๋ค๊ฐ, ์ฌ๋ฐ์ ๊ฒ ๊ฐ์์ (์ฌ์ค์ ๋น์ฉ์ ๊ฐ), AWS์์ 21๋ ๋ง์ ์๊ฐํ Virtual Waiting Room ์ํคํ ์ฒ๋ฅผ ๋์ ํ๊ธฐ๋ก ๊ฒฐ์ ํ์ต๋๋ค.
๊ธฐ์กด ์๋์ ๋ฐฑ์๋๋ ๋๋ถ๋ถ Serverless Framework๋ฅผ ์ด์ฉํ์ฌ ์๋ฒ๋ฆฌ์ค ์ํคํ
์ฒ๋ก ๊ตฌ์ฑ๋์ด ์์ด์ VWR๋ ์๋ฒ๋ฆฌ์ค๋ก ๊ตฌํํ๊ฒ ๋์์ต๋๋ค.
VWR ๊ตฌ์กฐ
AWS ๋ฌธ์์ ์๋ ์ํคํ ์ฒ๋ฅผ ๊ทธ๋๋ก ๊ตฌํํ์ง๋ ์๊ณ , ๋ถํ์ํ ๋ถ๋ถ๋ค์ ์ ๊ฑฐํ๊ณ ํ์ํ ๋ถ๋ถ๋ค์ ์ถ๊ฐํ๋ ๋ฑ ์ ํฌ ์ ๋ง์ ๋ง๊ฒ ์ฌ๊ตฌ์ฑํ๊ฒ ๋์์ต๋๋ค.
๊ตฌ์กฐ๋ฅผ ์ข ๊ฐ๋ตํ๊ฒ ์๊ฐ๋ฅผ ํด๋๋ฆฌ์๋ฉด, ์๋ ๊ทธ๋ฆผ์ฒ๋ผ ๋์ํ๊ฒ ๋ฉ๋๋ค.
- ApiGateway๋ฅผ ํตํด ์ฌ์ฉ์์ ์์ฒญ์ด ๋ค์ด์ค๋ฉด, SQS ๋ก ์์ฒญ ์ ๋ณด๋ฅผ ์์๋ก๋๋ค.
- ์ฌ์ฉ์๋ SQS ๋ฅผ ํตํด ๋ฐ์ ์๋ต์ MessageID ๋ก ์๋น์ค๊ฐ ๊ฐ๋ฅํ์ง, ์ผ๋ง๋ ๊ธฐ๋ค๋ ค์ผ ํ๋์ง๋ฅผ ๊ณ์ polling ํฉ๋๋ค.
- SQS ์ ์์ธ request ๋ค์ 10๊ฐ ๋จ์ ๋ฐฐ์น๋ก Lambda๋ฅผ ํตํด request ๋ณ๋ก ๋๊ธฐ ๋ฒํธ๋ฅผ ํ ๋นํฉ๋๋ค.
- ๋๊ธฐ ๋ฒํธ ์ค, ์๋น์ค๊ฐ ๊ฐ๋ฅํ ๋๊ธฐ ๋ฒํธ์ธ ๊ฒฝ์ฐ๋, polling ์์ฒญ์ด ์ค๋ฉด ์๋น์ค ํ ํฐ์ ๋ฐ๊ธํด์ค๋๋ค.
- ์๋น์ค ํ ํฐ์ ๋ฐ์ ์ฌ์ฉ์๋ ํด๋น ํ ํฐ์ผ๋ก ์ค์ ์๋น์ค๋ฅผ ์์ฒญํ๊ณ , ๊ตฟ์ฆ ๊ตฌ๋งค๋ ํฐ์ผํ ๋ฑ ์ํ๋ ์๋น์ค๋ฅผ ์ด์ฉํ ์ ์์ต๋๋ค.
- ์๋น์ค ์ด์ฉ์ด ๋๋ ์ฌ์ฉ์๋ ์๋ฃ api ๋ฅผ ๋ณด๋ด๊ณ , ์๋ฃ api ๋ฅผ ์ฒ๋ฆฌํ๋ Lambda ๋ ์๋น์ค ๊ฐ๋ฅํ ๋๊ธฐ ๋ฒํธ๋ฅผ ๋๋ ค์ค๋๋ค.
์์์ ๋ง์๋๋ ธ๋ฏ์ด VWR ์ ์ํคํ
์ฒ๋ ์๋ฒ๋ฆฌ์ค๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๊ณ ์์ต๋๋ค. (ApiGateway + Lambda)
๊ทธ๋ฆฌ๊ณ SQS์ Redis(ElasticCache)๊ฐ ์ด ์ํคํ
์ฒ์์ ํต์ฌ์ ์ธ ์ญํ ์ ํ๊ณ ์์ต๋๋ค.
SQS๋ ApiGateway Integration ๊ธฐ๋ฅ์ ํตํด, ApiGateway๋ก ๋ค์ด์ค๋ ์์ฒญ์ ๋ฐ๋ก ์ ์ฅํ๊ณ , ํด๋น ๋ฉ์์ง์ ๋ํ ์๋ต์ ์ค์ผ๋ก์จ ๋๋์ ํธ๋ํฝ์ ๋ํ ๋ฒํผ ์ญํ ์ ํ๊ฒ ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ Redis์ atomic increment ๊ธฐ๋ฅ์ ์ด์ฉํด์ ๋๊ธฐ์ด์ COUNTER ๊ฐ๋ ์ผ๋ก ๊ตฌํํฉ๋๋ค.
QUEUE_COUNTER | ์์ฒญ์ด ๋ค์ด์ค๋ฉด +1์ฉ ์ฆ๊ฐํฉ๋๋ค. ์์ฒญ๋ง๋ค atomicํ ํ๋์ ๋๊ธฐ ๋ฒํธ๋ฅผ ๊ฐ๊ฒ ๋ฉ๋๋ค. |
SERVING_COUNTER | ์ด๊ธฐ๊ฐ์ ์๋น์ค๋ฅผ ํ์ฉํ ์๋ก ์ค์ ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์๋น์ค๊ฐ ์๋ฃ๋ ์ฌ์ฉ์๊ฐ ์๊ธธ๋๋ง๋ค +1์ฉ ์ฆ๊ฐํฉ๋๋ค. |
TOKEN_COUNTER | ์๋น์ค ํ ํฐ์ ๋ฐ๊ธํ ๊ฐ์๋ฅผ ์ถ์ ํฉ๋๋ค. |
์๋ฅผ ๋ค์ด, ํ๋ฒ์ 100๋ช
์ ์๋น์ค๋ก ์ ํํ๋ค๊ณ ๊ฐ์ ํ๋ฉด,
SERVING_COUNTER=100, QUEUE_COUNTER=0 ์ผ๋ก ์์ํฉ๋๋ค.
๋ง์ฝ 300๋ช
์ ์์ฒญ์ด ๋ค์ด์ค๋ฉด, QUEUE_COUNTER=300 ๊น์ง ์ฌ๋ผ๊ฐ๊ณ ,
๊ทธ ์ค SERVING_COUNTER ๊ฐ๋ณด๋ค ์๊ฑฐ๋ ๊ฐ์ 1~100๋ฒ์ ์ฌ์ฉ์๋ค์ ์๋น์ค ํ ํฐ์ ๋ฐ๊ฒ ๋ฉ๋๋ค.
์๋น์ค๋ฅผ ์๋ฃํ ์ฌ์ฉ์๊ฐ ์๊ธฐ๋ฉด, SERVING_COUNTER๋ +1์ด ๋์ด์ 101์ด ๋๊ณ , ๊ทธ๋ฌ๋ฉด 101๋ฒ ์ฌ์ฉ์๋ ์๋น์ค ํ ํฐ์ ๋ฐ๊ฒ ๋ฉ๋๋ค.
์ค์ ์๋น์ค๋ฅผ ๊ตฌํํ๋ฉด์ ํค๋ฉจ๋ ๋ถ๋ถ์ 2๊ฐ์ง ๋ง์๋๋ฆฌ๊ณ ์ถ์ต๋๋ค.
์ฒซ๋ฒ์งธ๋ก ์ ๋จน์๋๊ฑด, ApiGateway ์ SQS ๋ฅผ ๋ฐ๋ก ์ฐ๊ฒฐํ๋ ์ค์ ์ด์์ต๋๋ค.
ํด๋น ๊ธฐ๋ฅ์ ํด์ฃผ๋ serverless plugin์ 2๊ฐ ๋ฐ๊ฒฌํ์ง๋ง, ์๋ํ์ง ์๊ฒ ๋์ง ์ค๋๋ ์ํ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ธฐ์กด์ ์ ํฌ๊ฐ ์ด๋ฏธ ์ฌ์ฉํ๋ ApiGateway v1 ๋ฒ์ ์์ ํด๋น ๊ธฐ๋ฅ์ ํ ์คํธํด๋ณด๋ ค๊ณ ๊ตฌ๊ธ๋ง์ ํ์ง๋ง ์ฝ๊ฒ ์ฐพ์ ์ ์์์ต๋๋ค.
๊ฒฐ๊ตญ serverless.yml ํ์ผ์์๋ ์ค์ ํ์ง ๋ชปํ๊ณ , ApiGateway ์์ ์ง์ resource์ method๋ฅผ ์์ฑํ๊ณ , request ๋ฅผ SQS๋ก ์ฐ๊ฒฐํ๊ณ , ์ผ๋ถ ์ค์ ๋ค์ ๋ฐ๊ฟ์ฃผ๋ ์์ผ๋ก ํ ์คํธ๋ฅผ ํด๋ณผ ์ ์์์ต๋๋ค.
ํ์ง๋ง, ApiGateway v2 ์์๋ serverless.yml ์์ ์์ฝ๊ฒ ์ค์ ์ด ๊ฐ๋ฅํฉ๋๋ค.
์ค์ ์ ๋ณด๋ ์๋์ ๊ฐ์ต๋๋ค.
provider:
httpApi:
name: VirtualWaitingRoom
functions:
AssignQueue:
handler: src/assign_queue/lambda.handler
events:
- sqs:
arn:
Fn::GetAtt:
- VWRQueue
- Arn
batchSize: 10
maximumBatchingWindow: 5
resources:
Resources:
VWRQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: VWRQueue
VWRApiRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId:
Ref: HttpApi
RouteKey: 'POST /start'
Target: !Join
- /
- - integrations
- !Ref VWRApiIntegration
VWRApiIntegration:
Type: AWS::ApiGatewayV2::Integration
Properties:
ApiId:
Ref: HttpApi
CredentialsArn:
Fn::GetAtt:
- VWRRole
- Arn
RequestParameters:
MessageBody: '$request.body'
QueueUrl:
Ref: VWRQueue
PayloadFormatVersion: '1.0'
IntegrationType: AWS_PROXY
IntegrationSubtype: SQS-SendMessage
ConnectionType: INTERNET
VWRRole:
Type: 'AWS::IAM::Role'
Properties:
RoleName: VWRRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Service: 'apigateway.amazonaws.com'
Action:
- 'sts:AssumeRole'
Policies:
- PolicyName: VWRPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
Action:
- sqs:SendMessage
Effect: Allow
Resource:
- Fn::GetAtt:
- VWRQueue
- Arn
v1์์ ํ ์คํธํ ๋์๋ ๋ค๋ฅด๊ฒ, v2์์๋ SQS์ ์๋ต์ด ์ ํฌ์๊ฒ ์ต์ํ json์ด ์๋ xml์ ๋ฐํํ๊ธฐ ๋๋ฌธ์,
ํด๋ผ์ด์ธํธ์์ xml ํ์ฑ์ด ํ์ํฉ๋๋ค.
๋๋ฒ์งธ๋ก ๊ณ ์ํ๋๊ฑด, ์๋น์ค ํ ํฐ์ ๋ฐ๊ธ ๋ฐ๊ณ ์ดํํ ์ฌ์ฉ์๊ฐ ์๊ฒผ์๋์ ์ฒ๋ฆฌ์์ต๋๋ค.
AWS ๋ฌธ์์์๋ ๋ฐ๊ธํ ํ ํฐ์ DynamoDB์ ์ ์ฅ์ ํด๋๊ณ , ๋ฐฐ์น๋ก ๋ง๋ฃ๋ ํ ํฐ์ ์ฒ๋ฆฌํ๊ณ ์์์ง๋ง, ์ ํฌ๋ ํด๋น ๊ธฐ๋ฅ์ Redis keyspace notifications ๋ฅผ ์ด์ฉํ๊ธฐ๋ก ํ๊ณ , ํ ํฐ์ Redis์ ์ ์ฅํ์ต๋๋ค.
expire ์๋ฆผ์ ๋ฐ๊ธฐ ์ํด์, expire ์ด๋ฒคํธ๋ฅผ subscribeํ๋ Lambda๋ฅผ ๋ง๋ค์๋๋ฐ, ๊ฒฐ๋ก ๋ถํฐ ๋ง์๋๋ฆฌ๋ฉด Lambda๋ฅผ ํตํด expire ์๋ฆผ์ ์ฒ๋ฆฌํ ์ ์์์ต๋๋ค.
subscribe์ ๋ฑ๋กํด๋ handler ํจ์์ async ๋ก์ง์ด ์ ๋๋ก ๋์ํ์ง ์์๊ณ , ์ธ๋ถ ํธ์ถ๋ ์ ๋์ํ์ง ์์์ต๋๋ค. ๊ฒฐ๊ตญ์ ํด๋น ๋ก์ง์ ECS๋ฅผ ํตํด ์๋น์คํ๋๋ก ๋ณ๊ฒฝํ์์ต๋๋ค.
๋ง๋ฌด๋ฆฌ
์ด๋ฒ ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ๊ทธ๋์ ๋ง์ด ๋ค๋ค๋ณด์ง ์์๋ ์๋ฒ๋ฆฌ์ค๋ SQS, Redis, ECS ์ ๋ํด ์ข ๋ ์๊ฒ ๋๊ณ , ๋ง์ด ๋ฐฐ์ธ ์ ์๋ ๊ณ๊ธฐ๊ฐ ๋์์ต๋๋ค. ๊ฐ์ธ์ ์ผ๋ก ๊ต์ฅํ ์ฌ๋ฐ๊ณ ์๋ฏธ์๋ ํ๋ก์ ํธ์๋ ๊ฒ ๊ฐ์ต๋๋ค.
๋ค์์ ๋ ๋ค๋ฅธ ํฅ๋ฏธ๋ก์ด ์ฃผ์ ๋ก ๋ง๋ ๊ธฐํ๋ฅผ ๊ธฐ๋ํ๊ฒ ์ต๋๋ค. ์์ผ๋ก๋ ๋ง์ ๊ด์ฌ ๋ถํ๋๋ฆฝ๋๋ค.
๋๊น์ง ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค. :D