1. 程式人生 > >flask-本地線程-請求上下文補充

flask-本地線程-請求上下文補充

ide 方法 bject oca 上下文 。。 修改 greenlet raise

context(上下文)是flask裏面非常好的設計,使用flask需要非常理解應用上下文和請求上下文這兩個概念

本地線程

本地線程(thread local)希望不同的線程對於內容的修改只在線程內部發揮作用,線程內部互相不影響

from django.test import TestCase
import threading

mydata = threading.local()
mydata.number = 42

print(mydata.number)

logs = []


def f():
    mydata.number = 11
    logs.append(mydata.number)


thread 
= threading.Thread(target=f) thread.start() thread.join() print(mydata.number)

技術分享圖片

可以看到,在線程的內部修改了mydata.number的值,但是沒有影響到開始設置的值

本地線程的實現原理就是:在threading.current_thread().__dict__裏添加一個包含對象mydata的id值的key,來保存不同的線程狀態

werkzeug的Local

werkzeug自己實現了自己的本地線程。werkzeug.local.Local和threading.local的區別如下:

werkzeug使用了自定義的__storage__來保存不同線程下的狀態

werkzeug提供了釋放本地線程的release_local方法

werkzeug使用了兩種方法來通過get_ident函數獲取線程的標識符
*(greenlet,系統線程,默認使用系統的,如果安裝了greenlet則使用她)

werkzeug還實現了兩種數據結構。

localstack:基於werkzeug.local.Local實現的棧結構,可以將對象推入,彈出,也可以快速拿到棧頂對象

localproxy:作用和名字一樣,是標準的代理模式。構造次結構時接收一個可以調用的參數(一般是函數),這個函數執行後就是通過localstack實例化的棧的棧頂對象。

     基於localproxy對象的操作實力上都會轉發到這個棧頂對象(也就是一個threadlocal上邊)

Flask.request

from
flask import Flask, request app = Flask(__name__) @app.route(/) def xxx(): name = request.args.get("name")

在這裏,我們先引用了flask.request,但是直到用戶訪問xxx函數的使用才通過request.args.get獲取請求的參數值,試想,引用的時候還沒發生這個請求,那麽請求上下文是怎麽獲得的呢?

flask.request就是一個獲取名為_request_ctx_stack的棧頂對象的LocalProxy實例:

from functools import partial
from werkzeug.local import LocalProxy

def _lookup_req_object(name):
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError("xxxxx")
    return getattr(top, name)

上面的邏輯可以正常使用,先來看看流程:

1:用戶訪問產生請求
2:在發生請求的過程中向_reqeust_ctx_stack推入這個請求的上下文對象,他會變成棧頂,request就會成為這個請求上下文,也就包含了這次請求的相關信息和數據
3:在視圖函數中使用request就可以使用request.args.get(name)了
設想不使用LocalStack和LocalProxy的話,要想讓視圖函數訪問的請求對象,就只能將其作為參數,一步步的傳入視圖函數中。這樣做的缺點是會讓每個視圖函數都增加一個request參數,
而flask卻巧妙的使用了上下文把某些對象設置成全局訪問,每個線程看到的上下文對象卻是不同的,這樣就巧妙的解決了這個問題

使用上下文

應用上下文的典型場景是緩存一些在發生請求之前要使用到的資源,比如生成數據庫鏈接和緩存一些對象;請求上下文發生在HTTP請求的開始,WSGI server調用Flask.__call__()之後。
應用上下文並不是應用啟動之後生成的唯一上下文

······有空寫吧,該吃飯了。。

flask-本地線程-請求上下文補充