플라스크에서 전역 변수는 스레드로부터 안전합니까? 요청간에 데이터를 공유하려면 어떻게합니까?
내 앱에서 공통 객체의 상태는 요청을 통해 변경되며 응답은 상태에 따라 다릅니다.
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 쿼리.
self.param
1 씩 증가합니다. - return 문이 실행되기 전에 스레드
self.param
는 클라이언트 2로 전환 됩니다. 다시 증가합니다. - 스레드는 클라이언트 1로 다시 전환되고 클라이언트는 숫자 2를 반환합니다.
- 이제 스레드가 클라이언트 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의 g
object를 사용할 수 있습니다 . 또 다른 일반적인 경우는 데이터베이스 연결을 관리하는 최상위 개체입니다. 이러한 유형의 "글로벌"에 대한 차이점 은 요청 간에 사용되지 않고 각 요청에 고유 하며 리소스의 설정 및 해체를 관리하는 것이 있다는 것입니다.
이것은 글로벌 스레드 안전성에 대한 답이 아닙니다.
하지만 여기서 세션을 언급하는 것이 중요하다고 생각합니다. 클라이언트 별 데이터를 저장하는 방법을 찾고 있습니다. 모든 연결은 스레드 세이프 방식으로 자체 데이터 풀에 액세스 할 수 있어야합니다.
이것은 서버 측 세션에서 가능하며 매우 깔끔한 플라스크 플러그인 ( 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
에이 작업을 실행할 수 있습니다. 다른 브라우저에서 액세스를 시도하면 카운터가 서로 공유되지 않는 것을 알 수 있습니다.
'Development Tip' 카테고리의 다른 글
왜`std :: initializer_list`는 종종 값으로 전달됩니까? (0) | 2020.11.28 |
---|---|
기본 Move 생성자가 noexcept로 정의되어 있습니까? (0) | 2020.11.28 |
컨트롤러 작업 방법에 대한 출력 캐시를 프로그래밍 방식으로 지우는 방법 (0) | 2020.11.28 |
Java에서 매개 변수 실행 순서 보장? (0) | 2020.11.28 |
모든 소스 하드 코딩 된 문자열 찾기 (0) | 2020.11.28 |