Development Tip

플라스크에서 전역 변수는 스레드로부터 안전합니까?

yourdevel 2020. 11. 28. 12:32
반응형

플라스크에서 전역 변수는 스레드로부터 안전합니까? 요청간에 데이터를 공유하려면 어떻게합니까?


내 앱에서 공통 객체의 상태는 요청을 통해 변경되며 응답은 상태에 따라 다릅니다.

class SomeObj():
    def __init__(self, param):
        self.param = param
    def query(self):
        self.param += 1
        return self.param

global_obj = SomeObj(0)

@app.route('/')
def home():
    flash(global_obj.query())
    render_template('index.html')

개발 서버에서 이것을 실행하면 1, 2, 3 등이 나올 것으로 예상됩니다. 100 개의 서로 다른 클라이언트에서 동시에 요청을하면 문제가 발생할 수 있습니까? 예상되는 결과는 100 개의 서로 다른 클라이언트가 각각 1에서 100까지의 고유 한 숫자를 보는 것입니다. 또는 다음과 같은 일이 발생합니다.

  1. 클라이언트 1 쿼리. self.param1 씩 증가합니다.
  2. return 문이 실행되기 전에 스레드 self.param클라이언트 2로 전환 됩니다. 다시 증가합니다.
  3. 스레드는 클라이언트 1로 다시 전환되고 클라이언트는 숫자 2를 반환합니다.
  4. 이제 스레드가 클라이언트 2로 이동하여 숫자 3을 반환합니다.

클라이언트가 두 개뿐이므로 예상 결과는 2와 3이 아니라 1과 2였습니다. 숫자를 건너 뛰었습니다.

애플리케이션을 확장 할 때 실제로 이런 일이 발생합니까? 전역 변수에 대한 대안은 무엇입니까?


이러한 종류의 데이터를 보유하기 위해 전역 변수를 사용할 수 없습니다. 스레드로부터 안전하지 않을뿐만 아니라 프로세스에 안전 하지도 않으며 프로덕션 환경의 WSGI 서버는 여러 프로세스를 생성합니다. 요청을 처리하기 위해 스레드를 사용하는 경우 카운트가 잘못되었을뿐만 아니라 요청을 처리 한 프로세스에 따라 달라질 수 있습니다.

Flask 외부의 데이터 소스를 사용하여 전역 데이터를 보관합니다. 데이터베이스, memcached 또는 redis는 필요에 따라 모두 적절한 별도의 스토리지 영역입니다. Python 데이터를로드하고 액세스해야하는 경우 multiprocessing.Manager. 사용자 당 간단한 데이터에 세션을 사용할 수도 있습니다.


개발 서버는 단일 스레드 및 프로세스에서 실행될 수 있습니다. 각 요청이 동 기적으로 처리되기 때문에 설명하는 동작을 볼 수 없습니다. 스레드 또는 프로세스를 활성화하면 볼 수 있습니다. app.run(threaded=True)또는 app.run(processes=10). (1.0에서는 서버가 기본적으로 스레드됩니다.)


일부 WSGI 서버는 gevent 또는 다른 비동기 작업자를 지원할 수 있습니다. 전역 변수는 여전히 대부분의 경쟁 조건에 대한 보호가 없기 때문에 스레드로부터 안전하지 않습니다. 한 작업자가 값을 얻고, 양보하고, 다른 작업자가 값을 수정하고, 양보 한 다음 첫 번째 작업자도 값을 수정하는 시나리오를 가질 수 있습니다.


요청 중에 일부 전역 데이터를 저장해야하는 경우 Flask의 gobject를 사용할 수 있습니다 . 또 다른 일반적인 경우는 데이터베이스 연결을 관리하는 최상위 개체입니다. 이러한 유형의 "글로벌"에 대한 차이점 요청 간에 사용되지 않고 각 요청에 고유 하며 리소스의 설정 및 해체를 관리하는 것이 있다는 것입니다.


이것은 글로벌 스레드 안전성에 대한 답이 아닙니다.

하지만 여기서 세션을 언급하는 것이 중요하다고 생각합니다. 클라이언트 별 데이터를 저장하는 방법을 찾고 있습니다. 모든 연결은 스레드 세이프 방식으로 자체 데이터 풀에 액세스 할 수 있어야합니다.

이것은 서버 측 세션에서 가능하며 매우 깔끔한 플라스크 플러그인 ( https://pythonhosted.org/Flask-Session/) 에서 사용할 수 있습니다.

세션을 설정하면 session모든 경로에서 변수를 사용할 수 있으며 사전처럼 작동합니다. 이 사전에 저장된 데이터는 각 연결 클라이언트에 대해 개별적입니다.

다음은 짧은 데모입니다.

from flask import Flask, session
from flask_session import Session

app = Flask(__name__)
# Check Configuration section for more details
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)

@app.route('/')
def reset():
    session["counter"]=0

    return "counter was reset"

@app.route('/inc')
def routeA():
    if not "counter" in session:
        session["counter"]=0

    session["counter"]+=1

    return "counter is {}".format(session["counter"])

@app.route('/dec')
def routeB():
    if not "counter" in session:
        session["counter"] = 0

    session["counter"] -= 1

    return "counter is {}".format(session["counter"])


if __name__ == '__main__':
    app.run()

이후 pip install Flask-Session에이 작업을 실행할 수 있습니다. 다른 브라우저에서 액세스를 시도하면 카운터가 서로 공유되지 않는 것을 알 수 있습니다.

참고URL : https://stackoverflow.com/questions/32815451/are-global-variables-thread-safe-in-flask-how-do-i-share-data-between-requests

반응형