Python

[Tornado] on_finish 함수 비동기화 (?)

비번변경 2024. 9. 4. 15:32

개요

Tornado로 개발한 애플리케이션에서 Redis를 비동기로 연결하여 사용하고 있다. 

from tornado.web import RequestHandler
import redis.asyncio as redis

class BaseHandler(RequestHandler):
    def prepare(self):
        self.redis_connection = redis.Redis()
        
    def on_finish(self):
        self.redis_connection.close()

그러나 테스트를 해보니 Redis의 연결을 종료하는 시점에 다음과 같은 에러가 발생하는 것을 발견했다.

/home/brian/repos/medigator/venv/lib/python3.8/site-packages/aioredis/client.py:1047: RuntimeWarning: coroutine 'Redis.close' was never awaited

확인해보니 redis.asyncio의 모든 함수는 코루틴이라 동기 함수를 호출하는 방식으로는 사용할 수 없어서 발생한 에러였다. 따라서 redis.close를 아래와 같이 asyncio.run 함수로 전달하여 수행하려고 했는데……

import asyncio

class BaseHandler(RequestHandler):
    def on_finish(self):
        asyncio.run(self.redis_connection.close())

이번에는 런타임에러가 발생했다.

RuntimeError: asyncio.run() cannot be called from a running event loop

찾아보니 asyncio.run 함수는 같은 스레드에서 다른 asyncio 이벤트 루프가 실행 중일 때는 사용할 수 없다고 한다.

처리할 방법을 찾아보다가 on_finish에서 동작해야 하는 코드를 비동기로 실행할  수 있는 방법이 없을까 고민하기 시작했다.

방법을 적어둔다.

 

 

on_finish는 비동기화 가능한가?

결론부터 말하자면 아니다.

Tornado RequestHandler는 비동기화할 수 있는 여러 함수를 제공하고 있는데, on_finish는 해당되지 않는다. 즉, async 키워드를 사용할 수 없다.

 

 

처리 방법

on_finish 함수 자체를 비동기로 생성할 수는 없지만, IOLoop.current().add_callback 함수를 사용하여 비동기로 코드를 실행하도록 할 수 있다.

비동기로 실행해야 하는 별도의 함수를 정의하고, on_finish 함수 내에서 IOLoop.current().add_callback로 수행하는 방법이다.

from tornado.ioloop import IOLoop

class BaseHandler(RequestHandler):
    async def on_finish_async(self):
        await self.redis_connection.close()

    def on_finish(self):
        IOLoop.current().add_callback(self.on_finish_async)

 

 

 

참고 문서

https://stackoverflow.com/questions/51375536/tornado-make-on-finish-asynchronous

asyncio.run() cannot be called from a running event loop 에러 질문 드립니다

https://www.tornadoweb.org/en/stable/ioloop.html#tornado.ioloop.IOLoop.add_callback