知識點 一、請求上下文 二、應用上下文python
Flask從客戶端收到請求時,要讓視圖函數能訪問一些對象,這樣才能處理請求。請求對象是一個很好的例子,它封裝了客戶端發送的HTTP請求。程序員
要想讓視圖函數可以訪問請求對象,一個顯而易見的方式是將其做爲參數傳入視圖函數,不過這會致使程序中的每一個視圖函數都增長一個參數,除了訪問請求對象,若是視圖函數在處理請求時還要訪問其餘對象,狀況會變得更糟。爲了不大量無關緊要的參數把視圖函數弄得一團糟,Flask使用上下文臨時把某些對象變爲全局可訪問。flask
request做爲全局對象就會出現一個問題,咱們都知道後端會開啓不少個線程去同時處理用戶的請求,當多線程去訪問全局對象的時候就會出現資源爭奪的狀況。也會出現用戶A的請求參數被用戶B請求接受到,那怎麼解決每一個線程只處理本身的request呢? 後端
import threading
import time
try:
from greenlet import getcurrent as get_ident # 協程
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident # 線程
class Local(object):
def __init__(self):
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident)
# 上面等價於self.__storage__ = {};self.__ident_func__ = get_ident;
# 可是若是直接賦值的話,會觸發__setattr__形成無限遞歸
def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value}
def __delattr__(self, name):
try:
del self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)
local_values = Local()
def func(num):
local_values.name = num
time.sleep(0.1)
print(local_values.name,threading.current_thread().name)
for i in range(1,20):
t = threading.Thread(target=func,args=(i,))
t.start()
print(local_values.__storage__)
複製代碼
1、請求到來時:bash
def request_context(self, environ):
return RequestContext(self, environ)
複製代碼
_request_ctx_stack = LocalStack()
_request_ctx_stack.push(self)
複製代碼
2、執行視圖時:session
request = LocalProxy(partial(_lookup_req_object, 'request'))
複製代碼
def __getattr__(self, name):
if name == '__members__':
return dir(self._get_current_object())
return getattr(self._get_current_object(), name)
複製代碼
__setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
複製代碼
說道底,這些方法的內部都是調用_lookup_req_object函數:去local中將ctx獲取到,再去獲取其中的method或header 3、請求結束:多線程
def pop(self, exc=_sentinel):
"""Pops the request context and unbinds it by doing that. This will also trigger the execution of functions registered by the :meth:`~flask.Flask.teardown_request` decorator. .. versionchanged:: 0.9 Added the `exc` argument. """
app_ctx = self._implicit_app_ctx_stack.pop()
try:
clear_request = False
if not self._implicit_app_ctx_stack:
self.preserved = False
self._preserved_exc = None
if exc is _sentinel:
exc = sys.exc_info()[1]
self.app.do_teardown_request(exc)
# If this interpreter supports clearing the exception information
# we do that now. This will only go into effect on Python 2.x,
# on 3.x it disappears automatically at the end of the exception
# stack.
if hasattr(sys, 'exc_clear'):
sys.exc_clear()
request_close = getattr(self.request, 'close', None)
if request_close is not None:
request_close()
clear_request = True
finally:
rv = _request_ctx_stack.pop()
# get rid of circular dependencies at the end of the request
# so that we don't require the GC to be active.
if clear_request:
rv.request.environ['werkzeug.request'] = None
# Get rid of the app as well if necessary.
if app_ctx is not None:
app_ctx.pop(exc)
assert rv is self, 'Popped wrong request context. ' \
'(%r instead of %r)' % (rv, self)
複製代碼
from flask import g:在一次請求週期裏,用於存儲的變量,便於程序員傳遞變量的時候使用。app
四個全局變量原理都是同樣的ide
# globals.py
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))
g = LocalProxy(partial(_lookup_app_object, 'g'))
複製代碼
當咱們request.xxx和session.xxx的時候,會從_request_ctx_stack._local取對應的值函數
當咱們current_app.xxx和g.xxx的時候,會從_app_ctx_stack._local取對應的值
from flask import Flask, request, g
app = Flask(__name__)
@app.route('/')
def index():
name = request.args.get('name')
g.name = name
g.age = 12
get_g()
return name
# 在一塊兒請求中,能夠用g變量傳遞參數
def get_g():
print(g.name)
print(g.age)
if __name__ == '__main__':
# 0.0.0.0表明任何能表明這臺機器的地址均可以訪問
app.run(host='0.0.0.0', port=5000) # 運行程序
複製代碼
當程序開始運行,而且請求沒到來的時候,就已經生成了兩個空的Local,即:
_request_ctx_stack = LocalStack() ->LocalStack類中的__init__定義了Local對象
_app_ctx_stack = LocalStack()
複製代碼
當同時有線程處理請求的時候,兩個上下文對應的Local對象變成以下:
_request_ctx_stack._local = {
線程id1:{‘stack’;[ctx1]}, # 只放一個爲何用list,實際上是模擬棧
線程id1:{‘stack’;[ctx2]},
...
}
_app_ctx_stack._local = {
線程id1:{‘stack’;[app_ctx1]},
線程id1:{‘stack’;[app_ctx2]},
...
}
複製代碼
歡迎關注個人公衆號: