PyCon Korea 2019
파이썬으로 서버를 극한까지 끌어다 쓰기
Async I/O의 밑바닥
한섬기
sync vs. 

async vs. 

blocking vs. 

non-blocking
•프로그램의 주 실행흐름을 멈추지 않고 진행할 수 있는가의 여부.

•코드의 실행 결과 처리 및 활용을 별도의 채널에 맡겨둔 뒤 결과를 기다리지 않고 

바로 다음 코드를 실행하는 방식으로 프로그램을 진행.

•예: Future, Promise, Coroutine 등.
!3
sync vs. async programming
•입출력 처리를 완료될 때까지 기다릴 것인지 혹은 시작만 해두고 

다음 작업을 계속 진행할 것인지 여부.

•I/O 작업이 완료된 이후에 연결해서 진행할 후속 작업이 있는 경우, 

Polling이나 Callback 함수를 사용.
!4
blocking vs. non-blocking I/O
•asynchronous programming vs. non-blocking I/O

•Blocking I/O를 사용했어도 이 부분이 별도의 채널을 통한 작업으로 이루어짐으로써 

어떤 프로그램의 주 실행흐름을 막지 않았다면, 

이 프로그램은 Asynchronous Programming으로 개발했다고 할 수 있음.
!5
async vs. non-blocking
Asynchronous Frameworks
•Quart

•Growler

•FastAPI
!7
etc.
•Flask와 유사한 프레임워크.

•가장 빠른 파이썬 웹 서버를 만드는 것이 목표.

•멀티 프로세스 아키텍쳐로, 멀티 CPU 코어를 기본적으로 사용.

•가능한 경우, uvloop과 C로 구현된 더 빠른 방식을 사용함.
!8
Vibora
•Flask와 유사한 프레임워크.

•가장 빠른 파이썬 웹 서버를 만드는 것이 목표.

•멀티 프로세스 아키텍쳐로, 멀티 CPU 코어를 기본적으로 사용.

•가능한 경우, uvloop과 C로 구현된 더 빠른 방식을 사용함.
!9
Vibora
이게 뭘까?
•asyncio를 사용하는 비동기 HTTP 클라이언트이자 서버 프레임워크.

•서버와 클라이언트 웹소켓을 둘 다 지원.

•웹서버는 미들웨어와 시그널을 제공.
!10
AIOHTTP
https://aiohttp.readthedocs.io/en/stable/
•Able to read and write cookies

•높은 성능의 HTTP서버를 쉽게 구축하고 확장할 수 있도록 만드는 것이 목표.

•클래스 기반의 뷰 제공

•블루프린트라는 기능을 통해 서브라우팅 제공.

•uvloop 기반의 비동기로 HTTP를 빠르게 처리할 수 있는 프레임워크.
!11
Sanic
•파이썬 웹 프레임워크이자 비동기 네트워크 라이브러리.

•HTTP의 클라이언트와 서버 둘 다 제공.

•코루틴 라이브러리인 tornado.gen 과 yield 문법을 통해 비동기를 구현.

•이 방식으로 인해 콜백 체이닝을 하지 않아도 코드를 깔끔하게 구현할 수 있었음.
!12
Tornado
Asynchronous Libraries
•asyncio는 async/await 구문을 사용하여 동시성 코드를 작성하는 라이브러리

•asyncio는 다음과 같은 작업을 위한 고수준 API 집합을 제공

•파이썬 코루틴들을 동시에 실행 및 제어

•자식 프로세스를 제어

•동시성 코드를 동기화

•저수준 API도 제공

•네트워킹, 자식 프로세스 실행, OS 시그널 처리 등의 

비동기 API를 제공하는 이벤트 루프를 만들고 관리
!14
asyncio
import asyncio
async def main():
print('Hello ...')
await asyncio.sleep(1)
print('... World!')
asyncio.run(main())
!15
asyncio
•uvloop은 Python의 asyncio에서 구현되어 있는 이벤트 루프를 대체함.

•uvloop은 Cython으로 구현되었으며, libuv를 기반으로 함.
!16
uvloop
import asyncio
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
!17
uvloop
•Unicorn Velociraptor Library.

•libuv는 크로스 플랫폼을 지원하는 라이브러리이며, 최초 Node.js를 위해 작성.

•이벤트 드리븐 비동기 I/O 모델을 기반으로 구성.

•kqueue, epoll, IOCP 등을 추상화.
!18
libuv
!19
http://docs.libuv.org/en/v1.x/design.html
•각각 다른 쓰레드에서 실행된다면 여러 이벤트 루프를 실행 가능.

•libuv의 이벤트 루프는 직접 언급되어 있지 않다면 안전하지 않은 쓰레드.

•모든 I/O는 각 OS에 맞는 non-blocking 소켓에서 수행.
!20
The I/O loop
!21
http://docs.libuv.org/en/v1.x/design.html
•이벤트 루프는 시간 관련 시스템 호출 수를 줄이기 위해 

이벤트 루프 틱 시작 시 현재 시각을 캐시.
22
•이벤트 루프가 활성 상태인 경우 반복이 시작되고, 그렇지
않으면 즉시 종료.

•루프에 활성 및 참조 처리된 핸들, 활성 요청 또는 닫히는 중
인 핸들이 있으면 활성 상태인 것으로 간주.
23
•마감 타이머가 시작됨.
24
•대기중인 콜백이 호출됨.

•I/O 콜백은 대부분 I/O 폴링 직후에 호출됨.

•그러나 다음 루프로 호출이 지연되는 콜백도 있음.

•이전 반복에서 지연된 I/O 콜백은 이 시점에서 호출됨.
25
•유휴(idle) 핸들 콜백이 호출됨.

•이름은 쉬고 있는 핸들이지만, 활성화 상태라면 모든 루프에
서 실행됨.
26
•핸들 준비 콜백이 호출됨.

•루프가 I/O를 차단(block)하기 직전에 준비 핸들이 

콜백을 호출함.
27
•I/O를 차단하기 전에 루프는 얼마나 오래 차단(block)해야 

하는지 폴링 타임아웃을 계산.

•루프가 I/O를 차단(block).

•이 시점에서 루프는 이전 단계에서 계산된 지속 시간 동안 

I/O를 차단(block).

•읽기 또는 쓰기 조작에 대해 주어진 파일 디스크립터를 

모니터링하고있는 모든 I/O 관련 핸들은 이 시점에서 

호출된 콜백을 가져옴.
28
•루프가 I/O를 차단(block)한 직후 체크 핸들이 콜백을 호출.

•체크 핸들은 기본적으로 준비 핸들과 대응됨.
29
•uv_close() 함수를 호출하여 핸들을 닫으면, 

닫기 콜백이 호출됨.
30
sync vs. async
Django vs. 

Flask vs. 

AIOHTTP vs. 

Sanic vs. 

Vibora
!33
사전 준비물
•AWS EC2 m5.2xlarge Instance - Dedicated Tenancy

•8 vCPU, 32 GiB Memory

•ubuntu 18.04

•https://github.com/vibora-io/benchmarks

•pip install 로 버전 지정없이 설치되는 최신 안정화 버전 웹 프레임워크들
!34
Forms Parsing
•multipart-form으로 몇 개의 파라미터와 

하나의 귀여운 파일을 POST로 전송하는 시나리오

•Vibora code
@app.route('/', methods=['POST'], cache=False)
async def home(request: Request):
return JsonResponse({'count': len(await request.files())})
!35
Forms Parsing
Frameworks Requests/Sec Version
Django 6896 2.2.4
Flask 12938 1.1.1
Aiohttp 2605 3.5.4
Sanic 47964 19.6.2
Vibora 81605 0.0.6
!36
Redis API
•JSON 데이터를 POST로 넘겨받아서 검증을 하고 

Redis에서 간단한 데이터를 하나 가져와서 반환하는 시나리오

•Vibora code
@app.route('/', methods=['POST'])
async def home(request: Request, redis: Redis):
schema = await SimpleSchema.load_json(request)
value = await redis.get(schema.key_name)
return Response(value)
!37
Redis API
Frameworks Requests/Sec Version
Django 11207 2.2.4
Flask 26793 1.1.1
Aiohttp 30213 3.5.4
Sanic 50825 19.6.2
Vibora 87269 0.0.6
asyncio vs. uvloop & libuv
•이벤트 루프의 모든 코드를 Cython으로 구현

•파이썬 객체를 모두 C의 구조체와 함수로 구현

•최적화된 메모리 관리

•시스템 자원 사용 최적화

•그 외의 기본적인 구현 방향은 거의 유사
39
왜 이벤트 루프에만 사용했을까?
41
•Django 3.0에서 ASGI와 함께 동작할 수 있는 완전한 비동기 방식을 지원할 예정

•Django 3.0은 2019년 12월 배포 예상

•동기 방식 코드를 그대로 사용하면 SynchronousOnlyOperation 에러를 볼 수 있음

•기존 코드는 거대한 마이그레이션이 필요할 것으로 보임
!42
Django X async
https://docs.djangoproject.com/en/dev/releases/3.0/#asgi-support
파이콘 한국 2019 - 파이썬으로 서버를 극한까지 끌어다 쓰기: Async I/O의 밑바닥

파이콘 한국 2019 - 파이썬으로 서버를 극한까지 끌어다 쓰기: Async I/O의 밑바닥

  • 1.
    PyCon Korea 2019 파이썬으로서버를 극한까지 끌어다 쓰기 Async I/O의 밑바닥 한섬기
  • 2.
    sync vs. asyncvs. blocking vs. non-blocking
  • 3.
    •프로그램의 주 실행흐름을멈추지 않고 진행할 수 있는가의 여부. •코드의 실행 결과 처리 및 활용을 별도의 채널에 맡겨둔 뒤 결과를 기다리지 않고 
 바로 다음 코드를 실행하는 방식으로 프로그램을 진행. •예: Future, Promise, Coroutine 등. !3 sync vs. async programming
  • 4.
    •입출력 처리를 완료될때까지 기다릴 것인지 혹은 시작만 해두고 
 다음 작업을 계속 진행할 것인지 여부. •I/O 작업이 완료된 이후에 연결해서 진행할 후속 작업이 있는 경우, 
 Polling이나 Callback 함수를 사용. !4 blocking vs. non-blocking I/O
  • 5.
    •asynchronous programming vs.non-blocking I/O •Blocking I/O를 사용했어도 이 부분이 별도의 채널을 통한 작업으로 이루어짐으로써 
 어떤 프로그램의 주 실행흐름을 막지 않았다면, 
 이 프로그램은 Asynchronous Programming으로 개발했다고 할 수 있음. !5 async vs. non-blocking
  • 6.
  • 7.
  • 8.
    •Flask와 유사한 프레임워크. •가장빠른 파이썬 웹 서버를 만드는 것이 목표. •멀티 프로세스 아키텍쳐로, 멀티 CPU 코어를 기본적으로 사용. •가능한 경우, uvloop과 C로 구현된 더 빠른 방식을 사용함. !8 Vibora
  • 9.
    •Flask와 유사한 프레임워크. •가장빠른 파이썬 웹 서버를 만드는 것이 목표. •멀티 프로세스 아키텍쳐로, 멀티 CPU 코어를 기본적으로 사용. •가능한 경우, uvloop과 C로 구현된 더 빠른 방식을 사용함. !9 Vibora 이게 뭘까?
  • 10.
    •asyncio를 사용하는 비동기HTTP 클라이언트이자 서버 프레임워크. •서버와 클라이언트 웹소켓을 둘 다 지원. •웹서버는 미들웨어와 시그널을 제공. !10 AIOHTTP https://aiohttp.readthedocs.io/en/stable/
  • 11.
    •Able to readand write cookies •높은 성능의 HTTP서버를 쉽게 구축하고 확장할 수 있도록 만드는 것이 목표. •클래스 기반의 뷰 제공 •블루프린트라는 기능을 통해 서브라우팅 제공. •uvloop 기반의 비동기로 HTTP를 빠르게 처리할 수 있는 프레임워크. !11 Sanic
  • 12.
    •파이썬 웹 프레임워크이자비동기 네트워크 라이브러리. •HTTP의 클라이언트와 서버 둘 다 제공. •코루틴 라이브러리인 tornado.gen 과 yield 문법을 통해 비동기를 구현. •이 방식으로 인해 콜백 체이닝을 하지 않아도 코드를 깔끔하게 구현할 수 있었음. !12 Tornado
  • 13.
  • 14.
    •asyncio는 async/await 구문을 사용하여 동시성 코드를 작성하는라이브러리 •asyncio는 다음과 같은 작업을 위한 고수준 API 집합을 제공 •파이썬 코루틴들을 동시에 실행 및 제어 •자식 프로세스를 제어 •동시성 코드를 동기화 •저수준 API도 제공 •네트워킹, 자식 프로세스 실행, OS 시그널 처리 등의 
 비동기 API를 제공하는 이벤트 루프를 만들고 관리 !14 asyncio
  • 15.
    import asyncio async defmain(): print('Hello ...') await asyncio.sleep(1) print('... World!') asyncio.run(main()) !15 asyncio
  • 16.
    •uvloop은 Python의 asyncio에서구현되어 있는 이벤트 루프를 대체함. •uvloop은 Cython으로 구현되었으며, libuv를 기반으로 함. !16 uvloop
  • 17.
  • 18.
    •Unicorn Velociraptor Library. •libuv는크로스 플랫폼을 지원하는 라이브러리이며, 최초 Node.js를 위해 작성. •이벤트 드리븐 비동기 I/O 모델을 기반으로 구성. •kqueue, epoll, IOCP 등을 추상화. !18 libuv
  • 19.
  • 20.
    •각각 다른 쓰레드에서실행된다면 여러 이벤트 루프를 실행 가능. •libuv의 이벤트 루프는 직접 언급되어 있지 않다면 안전하지 않은 쓰레드. •모든 I/O는 각 OS에 맞는 non-blocking 소켓에서 수행. !20 The I/O loop
  • 21.
  • 22.
    •이벤트 루프는 시간관련 시스템 호출 수를 줄이기 위해 
 이벤트 루프 틱 시작 시 현재 시각을 캐시. 22
  • 23.
    •이벤트 루프가 활성상태인 경우 반복이 시작되고, 그렇지 않으면 즉시 종료. •루프에 활성 및 참조 처리된 핸들, 활성 요청 또는 닫히는 중 인 핸들이 있으면 활성 상태인 것으로 간주. 23
  • 24.
  • 25.
    •대기중인 콜백이 호출됨. •I/O콜백은 대부분 I/O 폴링 직후에 호출됨. •그러나 다음 루프로 호출이 지연되는 콜백도 있음. •이전 반복에서 지연된 I/O 콜백은 이 시점에서 호출됨. 25
  • 26.
    •유휴(idle) 핸들 콜백이호출됨. •이름은 쉬고 있는 핸들이지만, 활성화 상태라면 모든 루프에 서 실행됨. 26
  • 27.
    •핸들 준비 콜백이호출됨. •루프가 I/O를 차단(block)하기 직전에 준비 핸들이 
 콜백을 호출함. 27
  • 28.
    •I/O를 차단하기 전에루프는 얼마나 오래 차단(block)해야 
 하는지 폴링 타임아웃을 계산. •루프가 I/O를 차단(block). •이 시점에서 루프는 이전 단계에서 계산된 지속 시간 동안 
 I/O를 차단(block). •읽기 또는 쓰기 조작에 대해 주어진 파일 디스크립터를 
 모니터링하고있는 모든 I/O 관련 핸들은 이 시점에서 
 호출된 콜백을 가져옴. 28
  • 29.
    •루프가 I/O를 차단(block)한직후 체크 핸들이 콜백을 호출. •체크 핸들은 기본적으로 준비 핸들과 대응됨. 29
  • 30.
    •uv_close() 함수를 호출하여핸들을 닫으면, 
 닫기 콜백이 호출됨. 30
  • 31.
  • 32.
    Django vs. Flaskvs. AIOHTTP vs. Sanic vs. Vibora
  • 33.
    !33 사전 준비물 •AWS EC2m5.2xlarge Instance - Dedicated Tenancy •8 vCPU, 32 GiB Memory •ubuntu 18.04 •https://github.com/vibora-io/benchmarks •pip install 로 버전 지정없이 설치되는 최신 안정화 버전 웹 프레임워크들
  • 34.
    !34 Forms Parsing •multipart-form으로 몇개의 파라미터와 
 하나의 귀여운 파일을 POST로 전송하는 시나리오 •Vibora code @app.route('/', methods=['POST'], cache=False) async def home(request: Request): return JsonResponse({'count': len(await request.files())})
  • 35.
    !35 Forms Parsing Frameworks Requests/SecVersion Django 6896 2.2.4 Flask 12938 1.1.1 Aiohttp 2605 3.5.4 Sanic 47964 19.6.2 Vibora 81605 0.0.6
  • 36.
    !36 Redis API •JSON 데이터를POST로 넘겨받아서 검증을 하고 
 Redis에서 간단한 데이터를 하나 가져와서 반환하는 시나리오 •Vibora code @app.route('/', methods=['POST']) async def home(request: Request, redis: Redis): schema = await SimpleSchema.load_json(request) value = await redis.get(schema.key_name) return Response(value)
  • 37.
    !37 Redis API Frameworks Requests/SecVersion Django 11207 2.2.4 Flask 26793 1.1.1 Aiohttp 30213 3.5.4 Sanic 50825 19.6.2 Vibora 87269 0.0.6
  • 38.
  • 39.
    •이벤트 루프의 모든코드를 Cython으로 구현 •파이썬 객체를 모두 C의 구조체와 함수로 구현 •최적화된 메모리 관리 •시스템 자원 사용 최적화 •그 외의 기본적인 구현 방향은 거의 유사 39
  • 40.
  • 41.
  • 42.
    •Django 3.0에서 ASGI와함께 동작할 수 있는 완전한 비동기 방식을 지원할 예정 •Django 3.0은 2019년 12월 배포 예상 •동기 방식 코드를 그대로 사용하면 SynchronousOnlyOperation 에러를 볼 수 있음 •기존 코드는 거대한 마이그레이션이 필요할 것으로 보임 !42 Django X async https://docs.djangoproject.com/en/dev/releases/3.0/#asgi-support