python-async image

ASGI Interface

ASGI는 단일 비동기 호출 가능한 객체로 구조화된다.

이 객체는 scope, send, receive 를 받는다.

  • scope : 특정 연결에 대한 세부 정보를 담고 있음. (dict형식)

  • send : 클라이언트에게 이벤트 메시지를 보내는 비동기 호출 가능 객체 (콜백)

  • receive : 클라이언트로부터 이벤트 메시지를 받는 비동기 호출 가능 객체 (콜백)

ASGI에서 주고받는 이벤트는 딕셔너리 형식이며, 미리 정의된 구조를 따른다. 각 이벤트는 type 키를 가지며, 이를 통해 이벤트의 성격을 파악할 수 있다 (예: HTTP, WebSocket 등).

이러한 형식으로 ASGI는 다양한 이벤트들이 들락날락하는걸 처리할 수 있을 뿐만 아니라, 다른 작업을 처리하는 백그라운드 코루틴(예: Redis 큐)도 동시에 수행할 수 있다.

가장 간단한 비동기 함수 애플리케이션은 다음과 같다.

# function-based
async def application(scope, receive, send):
    event = await recevie()
	...
	await send({'type': 'websocket.send', ...})

# instance-based
class App:
	async def __call__(self, scope, receive, send):
		assert scope['type'] == 'http'
app = App()

보내거나 받는 이벤트는 dict 이고, 미리 정의된 형식을 가진다.

이벤트들은 각각 정의된 type 키를 가지며, 이것을 가지고 이벤트의 구조를 유추할 수 있다. (e.g: http, websocket …)

recevie 에서 받을 수 있는 HTTP 요청의 본문을 포함하는 이벤트는 다음과 같으며, type키를 볼 수 있다.

{
	"type": "http.request",
	"body": b"Hello, World",
    "more_body": False
}
{
	"type": "websocket.send",
	"text": "Hello, World!"
}

들어오는 HTTP 요청은 연결 scope 를 다음과 같이 가지고있다.

{
    'type': 'http',
    'scheme': 'http',
    'root_path': '',
    'server': ('127.0.0.1', 8000),
    'http_version': '1.1',
    'method': 'GET',
    'path': '/',
    'headers': [
        (b'host', b'127.0.0.1:8000'),
        (b'user-agent', b'curl/7.51.0'),
        (b'accept', b'*/*')
    ]
}

이 때, send 코루틴을 사용하여 서버에 메시지를 보내는데 HTTP 응답을 두 단계로 나누어 처리한다.

await send({
    'type': 'http.response.start',
    'status': 200,
    'headers': [
        [b'content-type', b'text/plain'],
    ]
})
await send({
    'type': 'http.response.body',
    'body': b'Hello, world!',
})

이렇게 두 단계로 나누어 처리하면 응답 본문이 크거나 스트리밍이 필요한 경우에 유용하다.

예를 들어, 큰 파일을 전송하거나 실시간으로 데이터를 생성하는 경우에는 http.response.body 메시지를 여러 번 보내서 데이터를 분할 전송할 수 있다.

참조