이벤트 루프?
이벤트 루프(event loop) 혹은 메시지 디스패칭(message dispatching).
프로그램의 중앙에 위치한 제어 흐름
메시지가 수신될 떄마다, 아래의 코드에서 처리되며, quit 메시지가 수신되어 프로그램이 종료할 때까지 반복
while True:
message = get_message() if message == quit: break
process_message(message)
1. 기본 패턴
가장 많이 사용되는 이벤트 소스는 I/O (대부분의 I/O 는 본질적으로 블로킹 작업. 속도 매우 느림)
-
프로그램은 read 작업이 완료될 때까지 기다려야하며, 다른 작업 불가
-
read는 동기식 호출이며, 파일, 소켓 등에 읽을 데이터가 없다면 프로그램 블록
해결 방안
소켓에 읽을 데이터가 준비됐을 때, 이벤트를 발생시킨다.
import socket
s = socket.create_connection(("httpbin.org",80))
s.send(b"GET /delay/5 HTTP/1.1\r\nHost: httpbin.org\r\n\r\n")
buf = s.recv(1024) # 웹 서버가 응답 보내줄 때까지 멈춤
print(buf)
# http://httpbin.org/delay/5 에 HTTP요청을 보내고
# 서버는 5초 지연 후 JSON 응답
위의 코드처럼, 기다리는 동안 프로그램에 다른 작업을 시킬 수 있기 때문에, 입력이나 출력을 기다리는 상황은 피해야한다.
import socket
s = socket.create_connection(("httpbin.org", 80))
s.setblocking(False)
s.send(b"GET /delay/5 HTTP/1.1\r\nHost: httpbin.org/r/n/r/n")
buf = s.recv(1024)
print(buf)
비동기 모드로 실행 시, BlockingIOError가 발생한다.
결과적으로, 소켓에 데이터가 들어오자마자 프로그램이 바로 메시지를 가져올 수만 있다면,
기다리는 동안 다른 작업을 처리하면서 시간을 좀 더 효과적으로 보낼 수 있다.
# select.select(rlist, wlist, xlist) 함수는 임의 개수의 소켓(또는 파일 디스크립터)을
# 입력으로 받아서 읽기/쓰기 작업 가능 여부 또는 에러 발생 여부를 돌려준다.
import select
import socket
s = socket.create_connection(("httpbin.org", 80))
s.setblocking(False)
s.send(b"GET /delay/1 HTTP/1.1\r\nHost: httpbin.org\r\n\r\n")
while True:
ready_to_read, ready_to_write, in_error = select.select([s], [], [])
# 읽기 준비 상태 여부를 알기 위해 ready_to_read에 인수로 전달
# 블로킹 없이 데이터가 들어오면 곧바로 읽을 수 있다.
if s in ready_to_read:
buf = s.recf(1024)
print(buf)
break
select 호출 시에 이벤트 소스 여러 개를 결합하면, 프로그램을 쉽게 이벤트 주도 방식으로 만들 수 있다.
이런 방식은 한번에 수천 개의 연결을 처리해야 하는 프로그램의 핵심 요소로, NGINX나 Node.js 같은 매우 빠른 HTTP 서버에서 기반 기술로 사용되고 있다.
select는 오래된 기술이라, 파이썬은 asyncio라는 추상화 계층을 구현해서 제공.
2. asyncio 사용하기
- asyncio를 사용하려면, 파이썬 3.5 이상 버전이 필요하다.
- 이벤트 루프 개념을 기반
- 코루틴(coroutine) : 호출 측에 제어를 되돌려 줄수 있는 특별한 유형의 함수.
특정 이벤트(asyncio가 이벤트 루프 생성, 애플리케이션의 파일 읽기, 소켓의 쓰기작업 준비)가 발생했을 때 호출될 함수 등록
호출 측에 제어를 되돌려 줄 수 있는 특별한 유형의 함수
import asyncio
# 단순 def가 아닌, async def 로 정의
async def hellow_world():
print("hello world!")
return 42 # 코루틴이 결과를 돌려주면, 바로 종료되며 전체 프로그램 실행도 종료
hello_world_coroutine = hello_world()
print(hello_world_coroutine)
evend_loop = asyncio.get_event_loop()
try:
print("entering event loop")
result = event_loop.run_until_complete(hello_world_coroutine)
print(result)
finally:
event_loop.close()
코루틴은 코루틴 안에서 다른 코루틴을 호출할 수 있다.
import asyncio
import traceback
async def world(hello):
print("world") # 출력 2번
return hello + "world"
async def hello():
print("hello") # 출력 1번
result = await world("hello")
return result
event_loop = asynicio.get_event_loop()
try:
result = event_loop.run_until_complete(hello())
print(result) # 출력 3번
except Exception as ex:
print(ex)
print(traceback.format_exc())
finally:
event_loop.close()
'Developer > Python' 카테고리의 다른 글
[Python] Python에서 fpdf 사용하기 (0) | 2021.01.05 |
---|---|
[Python] 딕셔너리 키 값 존재여부 확인 (0) | 2021.01.04 |
[Python] 파이썬에서 IP 버전 확인하기 (0) | 2020.07.30 |
[Python] 파이썬에서 텔넷(telnet) 사용하기 (1) | 2020.07.29 |
[Python] dict 딕셔너리 키-값 출력 (0) | 2020.07.24 |