프로그래밍/Python

[Python] Flask 서버 멀티 스레드로 구동하기

채윤아빠 2023. 11. 10. 09:09
728x90
반응형

문제점 및 증상

Qt를 이용한 GUI와 함께 간단한 Flask 웹 서비스를 이용해야하는 상황이 있었습니다.

처음에는 다음과 같이 Flask를 MVC 형식으로 구현하고, Flask 실행부만 스레드로 구현하면 아무런 문제가 없을 줄 알고 실행해 보았습니다만, 다음과 같은 오류가 발생되고 정상적으로 실행되지 않았습니다.

Exception in thread Thread-6 (handleFlask):
Traceback (most recent call last):
  File "C:\Dev\Python\Python310\lib\threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "C:\Dev\Python\Python310\lib\threading.py", line 953, in run
    self._target(*self._args, **self._kwargs)
  File "D:\MyProj\python\python-test\http\flask\mvc-demo1\threaded_server.py", line 32, in handleFlask
    app.run(debug = True)
  File "d:\Dev\python\venv\myproj\lib\site-packages\flask\app.py", line 612, in run
    run_simple(t.cast(str, host), port, self, **options)
  File "d:\Dev\python\venv\myproj\lib\site-packages\werkzeug\serving.py", line 1099, in run_simple
  File "d:\Dev\python\venv\myproj\lib\site-packages\werkzeug\_reloader.py", line 439, in run_with_reloader
    signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))
  File "C:\Dev\Python\Python310\lib\signal.py", line 56, in signal
    handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
ValueError: signal only works in main thread of the main interpreter

해결 방안

해결 방법은 의외로 간단하였습니다. 아래 참고자료의 소스가 보기에는 복잡해 보이는데, 한 가지만 확인하면 됩니다.

핵심은 app.DEBUG 설정값이 False로 되어 있느냐 입니다. DEBUG 설정이 True로 되어 있으면 위에서 본 것과 같은 "ValueError" 오류가 발생합니다.

다음은 멀티 스레드로 MVC 패턴을 적용한 Flask 웹 서비스를 구동하는 간단한 예제입니다.

from flask import Flask
from threading import Thread
from time import sleep

from app.config.config import config
from app.controllers import register_apis, register_blueprint


def handleFlask() -> None:
    print("Flask handler started...\n")

    app = Flask(__name__)

    # DEBUG 설정을 반드시 False로 해주어야 signal 관련 오류가 발생하지 않습니다.
    config.DEBUG = False
    app.config.from_object(config) # load custom Config

    register_apis(app) # register RESTful APIs
    register_blueprint(app) # register Blueprint Controllers

    app.run() # 또는 app.run(debug = Flase)


def handleTimer() -> None:
    for index in range(5):
        print(f"  {index} passed...")
        sleep(1)

    print(f'timer end')


def main() -> None:
    Thread(target = handleFlask).start()

    Thread(target = handleTimer).start()


if __name__ == '__main__':
    main()

 

실행했을 때 결과는 다음과 같습니다.

  0 passed...
Flask handler started...

 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
  1 passed...
  2 passed...
  3 passed...
  4 passed...
timer end

참고자료