क्या वैश्विक चर धागे फ्लास्क में सुरक्षित हैं? मैं अनुरोधों के बीच डेटा कैसे साझा करूं?


101

मेरे एप्लिकेशन में अनुरोध करने से एक सामान्य वस्तु की स्थिति बदल जाती है, और प्रतिक्रिया राज्य पर निर्भर करती है।

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. रिटर्न स्टेटमेंट को निष्पादित करने से पहले, क्लाइंट 2 पर थ्रेड स्विच किया जाता self.paramहै।
  3. थ्रेड क्लाइंट 1 पर वापस जाता है, और क्लाइंट को नंबर 2, वापस दिया जाता है।
  4. अब थ्रेड क्लाइंट 2 पर जाता है और उसे नंबर 3 देता है।

चूंकि केवल दो ग्राहक थे, अपेक्षित परिणाम 1 और 2 थे, न कि 2 और 3. एक संख्या को छोड़ दिया गया था।

क्या वास्तव में ऐसा होगा जब मैं अपने आवेदन को बढ़ाऊंगा? वैश्विक चर के लिए मुझे क्या विकल्प देखना चाहिए?

जवाबों:


97

इस प्रकार के डेटा को रखने के लिए आप वैश्विक चर का उपयोग नहीं कर सकते हैं। न केवल यह थ्रेड सुरक्षित नहीं है , बल्कि यह सुरक्षित प्रक्रिया नहीं है , और उत्पादन में WSGI सर्वर कई प्रक्रियाओं को स्पॉन करता है। न केवल आपके मायने गलत होंगे यदि आप अनुरोधों को संभालने के लिए थ्रेड्स का उपयोग कर रहे थे, तो वे इस प्रक्रिया के आधार पर भिन्न भी होंगे जो अनुरोध को संभाला था।

वैश्विक डेटा रखने के लिए फ्लास्क के बाहर डेटा स्रोत का उपयोग करें। आपकी आवश्यकताओं के आधार पर एक डेटाबेस, मेमकास्टेड, या रेडिस सभी अलग भंडारण क्षेत्र हैं। यदि आपको पायथन डेटा को लोड करने और एक्सेस करने की आवश्यकता है, तो विचार करें multiprocessing.Manager। आप प्रति-उपयोगकर्ता सरल डेटा के लिए सत्र का उपयोग भी कर सकते हैं।


विकास सर्वर एकल थ्रेड और प्रक्रिया में चल सकता है। आपके द्वारा वर्णित व्यवहार को आप नहीं देखेंगे क्योंकि प्रत्येक अनुरोध को सिंक्रोनाइज़ किया जाएगा। धागे या प्रक्रियाओं को सक्षम करें और आप इसे देखेंगे। app.run(threaded=True)या app.run(processes=10)। (1.0 में सर्वर डिफ़ॉल्ट रूप से थ्रेडेड है।)


कुछ WSGI सर्वर जियोवेंट या किसी अन्य एसिंक्रोन वर्कर का समर्थन कर सकते हैं। वैश्विक चर अभी भी थ्रेड सुरक्षित नहीं हैं क्योंकि अधिकांश दौड़ स्थितियों के खिलाफ अभी भी कोई सुरक्षा नहीं है। आपके पास अभी भी एक परिदृश्य हो सकता है जहां एक कार्यकर्ता को एक मूल्य मिलता है, पैदावार होती है, दूसरा इसे संशोधित करता है, पैदावार करता है, फिर पहला कार्यकर्ता भी इसे संशोधित करता है।


यदि आपको किसी अनुरोध के दौरान कुछ वैश्विक डेटा संग्रहीत करने की आवश्यकता है , तो आप फ्लास्क की gवस्तु का उपयोग कर सकते हैं । एक अन्य सामान्य मामला कुछ शीर्ष-स्तरीय ऑब्जेक्ट है जो डेटाबेस कनेक्शन का प्रबंधन करता है। इस प्रकार के "वैश्विक" के लिए अंतर यह है कि यह प्रत्येक अनुरोध के लिए अद्वितीय है, अनुरोधों के बीच उपयोग नहीं किया जाता है , और संसाधन के सेट अप और फाड़ का प्रबंधन करने के लिए कुछ है।


30

यह वास्तव में ग्लोबल्स की धागा सुरक्षा का जवाब नहीं है।

लेकिन मुझे लगता है कि यहां सत्रों का उल्लेख करना महत्वपूर्ण है। आप क्लाइंट-विशिष्ट डेटा को संग्रहीत करने का तरीका ढूंढ रहे हैं। हर कनेक्शन को थ्रेडसेफ़ तरीके से डेटा के अपने पूल तक पहुंच होनी चाहिए।

यह सर्वर-साइड सत्रों के साथ संभव है, और वे बहुत साफ-सुथरे फ्लास्क प्लगइन में उपलब्ध हैं: 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, आपको इसे चलाने में सक्षम होना चाहिए। विभिन्न ब्राउज़रों से इसे एक्सेस करने का प्रयास करें, आप देखेंगे कि काउंटर उनके बीच साझा नहीं किया गया है।


3

उपरोक्त उत्तोलन के उत्तर को पूरी तरह से स्वीकार करते हुए, उत्पादन और स्केलेबल फ्लास्क स्टोरेज के लिए ग्लोबल्स के उपयोग को हतोत्साहित करने के लिए, प्रोटोटाइप या वास्तव में सरल सर्वरों के लिए, फ्लास्क 'डेवलपमेंट सर्वर' के तहत चल रहा है ...

... अजगर डेटा प्रकारों में निर्मित होता है, और मैंने व्यक्तिगत dictरूप से अजगर के डॉक्स ( https://docs.python.org/3/glossary.html#term-global-interpreter-lock ) के अनुसार वैश्विक रूप से उपयोग और परीक्षण किया है। धागा सुरक्षित। सुरक्षित प्रक्रिया नहीं ।

इस तरह (सर्वर ग्लोबल) से आवेषण, लुकअप, और रीड्स विकास सर्वर के तहत चल रहे प्रत्येक (संभवतः समवर्ती) फ्लास्क सत्र से ठीक होंगे।

जब इस तरह के वैश्विक तानाशाह को अद्वितीय फ्लास्क सत्र कुंजी के साथ रखा जाता है, तो यह सत्र विशिष्ट डेटा के सर्वर साइड स्टोरेज के लिए उपयोगी हो सकता है अन्यथा कुकी (अधिकतम आकार 4k) में फिटिंग नहीं करता है।

बेशक, इस तरह के सर्वर ग्लोबल डिक्टेट को बहुत बड़े होने के लिए सावधानी से संरक्षित किया जाना चाहिए, स्मृति में होना। अनुरोध प्रसंस्करण के दौरान 'पुरानी' कुंजी / मान युग्म को समाप्त करने के कुछ प्रकारों को कोडित किया जा सकता है।

फिर से, उत्पादन या स्केलेबल परिनियोजन के लिए अनुशंसित नहीं है, लेकिन संभवतः स्थानीय कार्य उन्मुख सर्वरों के लिए ठीक है जहां दिए गए कार्य के लिए अलग डीबी बहुत अधिक है

...

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.