Zettelkasten

비동기 프레임워크는 head-of-line blocking과 C10K 두 문제를 해결한다

·수정 1

요약

  • 비동기/이벤트 루프 모델이 등장한 이유는 두 가지로 정리됨: ① 무거운 요청의 head-of-line blocking, ② 대량 동시 연결의 C10K
  • 둘 다 본질은 "I/O를 기다리는 동안 스레드/CPU를 놀리지 않는다"는 한 문장으로 수렴함

본문

1. Head-of-line blocking — 무거운 요청이 뒤를 막는다

  • 동기 + 제한된 워커(스레드/프로세스 풀) 모델에서는 워커 하나가 요청 하나를 끝까지 잡고 있음
  • 무거운 요청(느린 외부 API, 큰 쿼리 등)이 워커를 점유하면, 그 뒤에 줄 선 가벼운 요청들이 워커가 빌 때까지 전부 대기함
  • 워커 수가 N개면 동시에 막힐 수 있는 무거운 요청도 N개로 제한됨 → 풀 고갈
  • 비동기의 해법: I/O 대기 지점에서 await로 실행권을 이벤트 루프에 반환 → 무거운 요청이 I/O 응답을 기다리는 동안 같은 스레드가 다른 요청을 처리함. 점유가 풀려 줄막힘이 사라짐

2. C10K — 연결 수가 늘면 스레드 비용이 폭발한다

  • blocking I/O + 스레드/프로세스 per connection 모델은 연결 하나당 전담 스레드를 붙임
  • 연결이 수천~수만 개가 되면 스레드 수도 그만큼 늘어나고, 각 스레드의 스택 메모리 + context switching 비용이 감당 안 됨 (Dan Kegel, 1999 C10K Problem)
  • 게다가 I/O-bound 워크로드에서는 스레드 대부분이 그냥 응답을 기다리며 놀고 있는데 자원만 잡아먹음
  • 비동기의 해법: 단일(혹은 소수) 스레드 + 이벤트 루프로, 블로킹 대기를 코루틴/콜백으로 바꿈. 연결당 비용이 스택이 아니라 작은 객체 수준으로 떨어져 적은 스레드로 수만 동시 연결을 버팀
  • 이 이벤트 루프의 하부 메커니즘이 I/O multiplexing(epoll) — 자세한 건 아래 링크 노트 참조

공통 본질

  • 두 문제 모두 "스레드를 I/O 대기에 묶어두는 것"이 원인
  • 비동기는 대기 지점을 yield point로 만들어 스레드를 대기에서 풀어줌으로써 둘 다 해결함

관련 노트

참고