asyncio와 aiohttp로 내려받기

매일 공부(ML)·2023년 5월 24일
0

Fluent Python

목록 보기
126/130

제어 흐름

asyncio를 이용한 동시성

asyncio와 aiohttp로 내려받기

asyncio는 TCP와 UDP만 직접 지원하고, HTTP등의 프로토콜을 지원하려면 서드파티 패키지가 필요하며 현재 비동기 HTTP 클라이언트/서버를 구현하는 사람들은 모두 aiohttp를 사용하는 것 같다.

#flags_asyncio.py: asynico와 aiohttp를 사용한 비동기 내려받기 스크립트
"""
1. download_one()을 호출해서 생성된 여러 코루틴 캑체를 이벤트 루프에 넣어 download_many()안에서 프로세스를 시작한다.
2. asyncio 이벤트 루프는 각각의 코루틴을 차례대로 활성화한다
3. get_flag()등의 클라이어튼 코루틴이 aiohttp.request()등의 라이브러리 코루틴에 위임하기 위해 yield from을 사용하면, 제어권이 이벤트 루프로 넘어가서, 이벤트 루프가 이전에 스케줄링딘 다른 코루틴을 실행
4. 블로킹된 연산이 완료되었을 때 통지받기 위해, 이벤트 루프는 콜백에 기반한 저수준API
5.연산이 완료되면 메인 루프는 결과를 중단된 코루틴에 보낸다.
6. 루프가 다시 제어권으로 가져오고 종료될 때까지 4~6단계 반복
"""

import asyncio
import aiohttp
from flags import BASE_URL, save_flag, show, main

@asyncio.coroutine
def get_flag(cc):
	url = '{}/{cc}/{cc}.gif'.format(BASE_URL, cc=cc.loweR())
    resp = yield from aiohttp.request("GET",url)
    image = yield from resp.read()
    return image
    
@asyncio.coroutine
def donload_one(cc):
	image = yield from get_flag(cc)
    show(cc)
    save_flage(image, cc.lower() + '.gif')
    return cc
    

def download_many(cc_list):
	loop = asyncio.get_event_loop()
    to_do = [download_one(cc) for cc in sorted(cc_list)]
    wait_coro = asyncio.wait(to_do)
    res, _ = loop.run_until_complete(wait_coro)
    loop.close()
    return len(res)
  
if __name__ = '__main__':
	main(download_many)

asyncio.wait ( ) 코루틴은 Future 객체나 코루틴의 반복형을 받고, wait ( )는 각 코루틴을 Task 안에 래핑한다. 결국 wait( )가관리하는모든객체는 Future 객체가된다. wait( )는코
루틴 함수이기 때문에 이를 호출하면 코루틴/제너레이터 객체가 반환된다. wait_coro 변수에
들어 있는 게 바로 wait ( ) 가 반환한 코루틴 제너레이터 객체다. 이 코루틴을 구동하기 위해 그 것을 loop .run_until_complete ( ) 에 전달한다. loop.run_until_complete ( ) 함수는 Future 객체나 코루틴을 받는다.

코루틴을 받으면wait ( )가 하는 것과 비슷하게 run_until_complete ( )도 코루틴을 Task 안에 래핑한다. 코루틴, Future, Task는 모두 yield from으로 구동할 수 있으며, 이것이 바로 wait ( ) 가 반환 한 wait_coro 객체에 run_until_complete ( ) 가 하는 일이다. wait_coro는 실행이 완료되면 (<실행 완료된 Future들의 집합>,<실행이 완료되지 않은 Future 들의 집합>) 튜플 끝 반환한·다·.


asyncio는 "실눈을 뜨고 보면서 yield from 키워드가 없는 것처럼 생각하기
=>구현한 평범한 순차 버전의 코드처럼 읽는 것이 쉽다.

@asyncio.coroutine
def get_flag(cc):
	url = '{}/{cc}/{cc}/gif'.format(BASE_URL, cc=cc.lower())
    resp = yield from aiohttp.request('GET',url)
    image = yield from resp.read()
    return image
    

asyncio 자체에 의해 구동되며, 체인을 통해 궁극적으로 가장 안쪽에 있는 하위 제너레이터
는 (aiohttp 등의 서드파티 라이브러리를 경유해서) asyncio 라이브러리가 제공하는· 코루틴
에 위임한다. 즉, asyncio 이벤트 루프가 코루틴 체인을 구동하고, 코루틴 체인은 결국 저수준
비동기 입출력을 수행하는 라이브러리 함수에서 끝난다.

profile
성장을 도울 아카이빙 블로그

0개의 댓글