fastapi 비동기 처리 worker가 timeout으로 kill 될 때 해결방안

항상 정리하기·2022년 4월 1일
0

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

위 링크에 제시된 다른 방법도 검토하고 테스트도 해봤는데..

  1. Use more workers (e.g. uvicorn main:app --workers 4). This will allow up to 4 somelongcomputation in parallel.
  • worker를 늘리더라도 timeout은 피할 수 없는 구조라서 제외
  1. Rewrite your task to not be async (i.e. define it as def task(data): ... etc). Then starlette will run it in a separate thread.
  • path 처리 함수부터 이미 async로 정의되고 거의 대부부의 함수를 async로 정의한 상태라서 전부 다 수정해야 해서 제외
  1. Use fastapi.concurrency.run_in_threadpool, which will also run it in a separate thread
  • 선택
  1. use asyncios's run_in_executor directly (which run_in_threadpool uses under the hood)
  • 테스트해봤는데 문제가 해결되지 않아서 제외
  1. Spawn a separate thread / process yourself. E.g. using concurrent.futures.
  • 테스트해봤는데 문제가 해결되지 않아서 제외
  1. Use something more heavy-handed like celery.
  • celery라서 제외
profile
늦은 것 같지만 이제부터라도 차근차근 하나씩 정리하기

0개의 댓글