Django, flask에서 비동기 처리 방법을 찾아보면 주로 celery를 활용하는 방법이 검색된다. celery를 내가 잘 몰라서 그런거겠지만 설정할 게 굉장히 많고(복잡하고) 메시지 큐(redis, rabbitmq 또는 rdb도 가능하다고는 하는데..)도 필요하다.
fastapi는 자체적으로 background 기능을 제공한다.
https://fastapi.tiangolo.com/tutorial/background-tasks/
정말 오래걸리는 작업을 별도 프로세스/서버로 실행해야 한다면 celery가 좋은 방법이 되겠지만 나는 단순하지만 약간의 지연을 발생시키고 실시간으로 처리될 필요는 없는 그런 작업을 비동기로 처리하고자 했고 fastapi의 BackgroundTask를 최적의 선택지로 판단했다.
요청을 받고 오래 걸리는 작업은 background로 보내놓고 background로 넘어간 작업은 기다리지 않고 바로 응답할 수 있어서 불필요한 지연을 제거할 수 있다.
fastapi 거의 대부분의 기능이 그러하듯 사용법은 정말 간단하다.
path 처리 함수에 BackgroundTask를 파라미터로 정의하고 함수 안에서는 BackgroundTask에 오래 걸리는 작업을 추가(addTask)만 하면 된다.
https://fastapi.tiangolo.com/tutorial/background-tasks/#using-backgroundtasks
처음엔 간단하게 rdb에 처리 이력을 저장하는 용도로 BackgroundTask를 사용했는데 처리 시간이 오래 걸리는 작업도 비동기로 처리할 필요가 생겼다. celery를 추가하기엔 너무 과한것 같아서 마찬가지로 BackgroundTask로 처리했다.
잘 실행되는 듯 했는데 gunicorn_error.log를 확인해보니 gunicorn timeout을 넘어가는 경우 해당 worker가 죽임(?)을 당했다.
[2017-01-21 14:07:37 +0000] [26635] [CRITICAL] WORKER TIMEOUT (pid:27202)
[2017-01-21 14:07:37 +0000] [26635] [CRITICAL] WORKER TIMEOUT (pid:27205)
이제와서 celery, mq를 추가 적용해야 하나 걱정하면서 열심히 찾아봤는데 아래 링크에 제시된 방법 중 3번째 fastapi.concurrency.run_in_threadpool
를 적용해서 해결했다.
https://stackoverflow.com/questions/67599119/fastapi-asynchronous-background-tasks-blocks-other-requests
위 링크에 제시된 다른 방법도 검토하고 테스트도 해봤는데..