LabHub

큐와 비동기 API · 백프레셔와 처리량 · 이론

λ, μ, 그리고 큐 깊이

LabHub 에서 이어서 보기

한 줄 요약

안정 조건은 λ < μ 하나다. 유입률이 처리율을 넘는 순간 큐 깊이는 선형이 아니라 폭발적으로 자란다.

왜 이게 필요했나

큐 깊이 그래프를 보면 이상한 점이 있습니다. 부하가 서서히 오르는데 큐 깊이는 한동안 0 근처에 붙어 있다가, 어느 지점을 넘으면 갑자기 수직으로 치솟습니다. 이것은 버그가 아니라 대기 행렬의 성질입니다.

유입률 λ 가 처리율 μ 보다 작으면 큐는 대체로 비어 있습니다. 둘이 가까워질수록 순간적인 변동을 흡수하느라 평균 깊이가 커지고, λ 가 μ 를 넘으면 큐는 무한히 자랍니다. 이용률 ρ = λ/μ 가 0.7 일 때와 0.95 일 때 대기 시간의 차이는 두 배가 아니라 훨씬 큽니다.

그래서 워커 용량을 유입의 딱 100% 로 맞추면 안 됩니다. 70~80% 이용률을 목표로 잡고 여유를 남기는 것이 실무의 관행입니다.

어떻게 동작하나

큐 깊이를 지연으로 번역하는 도구가 리틀의 법칙입니다. L = λ x W. 시스템 안의 평균 항목 수는 유입률 곱하기 평균 체류 시간입니다. 뒤집으면 W = L / λ 입니다. 큐 깊이가 3,000 이고 처리율이 초당 50건이면 지금 들어온 메시지는 60초 뒤에 처리됩니다. 이 계산이 되면 "큐가 좀 쌓였네"가 "지금 접수하는 사용자는 1분을 기다린다"로 바뀝니다.

백프레셔는 이 상황에서 시스템이 스스로를 지키는 방법입니다. 세 층위가 있습니다.

첫째, 큐 상한. 큐에 최대 길이를 두고 초과하면 생산자에게 429 를 돌려줍니다. 무한히 받아 놓고 나중에 못 하는 것보다, 지금 못 받는다고 말하는 편이 정직합니다.

둘째, 부하 차단(load shedding). 우선순위가 낮은 요청부터 버립니다. 모든 요청을 붙잡고 느리게 처리하다 다 같이 죽는 것보다, 일부를 빨리 거절하고 나머지를 살리는 편이 전체 가용성에 이롭습니다.

셋째, 소비자 동시성 조절. μ 를 올립니다. 다만 무한정 올릴 수는 없습니다 — 워커를 늘리면 그 뒤의 DB 나 외부 API 가 새 병목이 됩니다. 병목을 옮긴 것인지 없앤 것인지 매번 확인해야 합니다.

현장에서 만나는 모습

429 를 돌려줄 때 Retry-After 헤더를 함께 주면 좋습니다. 눈치 빠른 클라이언트는 이 신호를 존중해 불필요한 조기 재시도를 삼갑니다. 이것을 안 주면 클라이언트는 자기 나름의 백오프로 재시도하고, 그것이 서로 겹칩니다.

그리고 큐 깊이 알림은 절대값보다 추세로 거는 편이 낫습니다. 깊이 1,000 이 정상인 시스템도 있고 10 이 비정상인 시스템도 있습니다. "5분간 계속 증가"가 훨씬 신뢰할 만한 신호입니다.

다음 실습에서 할 것

부하 생성기로 큐를 채우면서 깊이를 시계열로 기록하고, 리틀의 법칙으로 대기 시간을 추정하고, 큐 상한과 429 를 붙이고, 워커 동시성을 올려 처리율 개선을 측정한 뒤 안정 조건을 문서로 정리합니다.