1.Flask上下文分爲application context和request context,即應用上下文和請求上下文,這二者都處於同一請求的局部中python
/usr/lib/Python27/lib/site-packages/flask/ctx.pyflask
class AppContext(object): def __init__(self, app): self.app = app self.url_adapter = app.create_url_adapter(None) self.g = app.app_ctx_globals_class() # Like request context, app contexts can be pushed multiple times # but there a basic "refcount" is enough to track them. self._refcnt = 0
說明:AppContext類即爲程序上下文,內部保存幾個變量,app爲當前程序實例,g用來保存每一個請求中須要用到的請求內全局變量session
/usr/lib/Python27/lib/site-packages/flask/ctx.py多線程
class RequestContext(object): def __init__(self, app, environ, request=None): self.app = app if request is None: request = app.request_class(environ) self.request = request self.url_adapter = app.create_url_adapter(self.request) self.flashes = None self.session = None # Request contexts can be pushed multiple times and interleaved with # other request contexts. Now only if the last level is popped we # get rid of them. Additionally if an application context is missing # one is created implicitly so for each level we add this information self._implicit_app_ctx_stack = []
說明:RequestContext即請求上下文,內部保存着幾個變量,app爲當前程序實例,request爲請求對象,session爲會話對象併發
1.Flask是多線程,線程有個叫ThreadLocal的類,用於實現線程隔離的類,而Werkzeug本身實現了它的線程隔離類,Werkzeug.local.Local,LocalStack就是Local實現的app
/usr/lib/Python27/lib/site-packages/flask/globals.pyide
_request_ctx_stack = LocalStack() _app_ctx_stack = LocalStack()
說明:LocalStack是Flask定義的線程隔離的棧(先進後出)存儲對象,分別用來存儲程序上下文和請求上下文,對於不一樣的線程,它們訪問這兩個對象看到的結果是不同的,徹底隔離,每一個傳給Flask對象的請求,都在不一樣的線程中處理,並且同一時刻每一個線程只處理一個請求,因此對於每一個請求來講,它們徹底不用擔憂本身上下文中的數據被別的請求所修改函數
問題:Flask中的g,session,request,current_app是怎麼作到同一個對象能在全部請求中使用而不會衝突哪?this
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'))
說明:LocalProxy類的構造函數接受一個callable參數,如上傳遞一個偏函數,以g爲例,當對g進行操做時,就會調用做爲參數的偏函數,併發操做轉換到偏函數返回的對象上url
def _lookup_req_object(name): top = _request_ctx_stack.top if top is None: raise RuntimeError('working outside of request context') return getattr(top, name) def _lookup_app_object(name): top = _app_ctx_stack.top if top is None: raise RuntimeError('working outside of application context') return getattr(top, name) def _find_app(): top = _app_ctx_stack.top if top is None: raise RuntimeError('working outside of application context') return top.app
說明:_app_ctx_stack和request_ctx_stack都是線程隔離的,因此流程是這樣,訪問g->從當前線程的程序上下文棧頂獲取程序上下文->取出其中的g對象->進行操做,因此能夠經過一個g對象讓全部線程互不干擾的訪問本身的g
def push(self): top = _request_ctx_stack.top if top is not None and top.preserved: top.pop(top._preserved_exc) # Before we push the request context we have to ensure that there # is an application context. app_ctx = _app_ctx_stack.top if app_ctx is None or app_ctx.app != self.app: app_ctx = self.app.app_context() app_ctx.push() self._implicit_app_ctx_stack.append(app_ctx) else: self._implicit_app_ctx_stack.append(None) _request_ctx_stack.push(self)
說明:構建Flask對象後並不會推送上下文,而在Flask對象調用run()做爲WSGI應用啓動後,每當有請求進入時,推送請求上下文以前,首先會檢查棧頂如已存在應用上下文,若是存在,判斷與請求上下文是否屬於同一個應用,若是不存在就會推送一個當前應用的上下文,若是是單WSGI應用的程序,此判斷毫無心義
def pop(self, exc=None): app_ctx = self._implicit_app_ctx_stack.pop() ……………… ……………… rv = _request_ctx_stack.pop() # Get rid of the app as well if necessary. if app_ctx is not None: app_ctx.pop(exc)
說明:請求結束時,調用auto_pop函數,其中又調用自身pop函數,會把請求上下文和程序上下文都pop掉,因此能夠得出結論,在單WSGI應用環境下,每一個請求獨立於線程上曾經的請求,獨立與其它線上的請求
app_ctx = app.app_context() try: # 說明: 嘗試從LocalStack獲取當前線程對應的程序上下文current_app,發現爲空拋出運行時錯誤 print current_app.name except RuntimeError as e: # 說明: 程序上下文沒有壓入LocalStack棧 print 'found errors: %s' % (e) # 說明: 壓入程序上下文到棧 app_ctx.push() # 說明: 由於上面壓入了棧因此能夠訪問到 print current_app.name