国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

應(yīng)用服務(wù)器與WSGI協(xié)議以及flask后端框架總結(jié)(后端接收請(qǐng)求返回響應(yīng)的整個(gè)流程)

tolerious / 2391人閱讀

摘要:上次遺留了兩個(gè)問(wèn)題先說(shuō)一下自己的看法問(wèn)題明明一個(gè)線程只能處理一個(gè)請(qǐng)求那么棧里的元素永遠(yuǎn)是在棧頂那為什么需要用棧這個(gè)結(jié)構(gòu)用普通變量不行嗎和都是線程隔離的那么為什么要分開(kāi)我認(rèn)為在的情況下是可以不需要棧這個(gè)結(jié)構(gòu)的即使是單線程下也不需要原本我以為在

上次遺留了兩個(gè)問(wèn)題,先說(shuō)一下自己的看法
問(wèn)題:
1.明明一個(gè)線程只能處理一個(gè)請(qǐng)求,那么棧里的元素永遠(yuǎn)是在棧頂,那為什么需要用棧這個(gè)結(jié)構(gòu)?用普通變量不行嗎.
2._request_ctx_stack和_app_ctx_stack都是線程隔離的,那么為什么要分開(kāi)?
我認(rèn)為在web runtime的情況下是可以不需要棧這個(gè)結(jié)構(gòu)的,即使是單線程下也不需要,原本我以為在單線程下,當(dāng)前一個(gè)請(qǐng)求阻塞后,后一個(gè)請(qǐng)求還會(huì)被推入棧中,結(jié)果并不是這樣,這也就說(shuō)明了,棧的結(jié)構(gòu)和是不是單線程沒(méi)關(guān)系,為了驗(yàn)證這點(diǎn),我寫(xiě)了個(gè)簡(jiǎn)單的接口驗(yàn)證這點(diǎn):

from flask import Flask,_request_ctx_stack

app = Flask(__name__)


@app.route("/")
def index():
    print(_request_ctx_stack._local.__storage__)
    time.sleep(1)
    return "

hello

" app.run(port=3000)

def wsgi_app(self, environ, start_response):

ctx = self.request_context(environ)
ctx.push()
print(_request_ctx_stack._local.__storage__)

我在Flask類中的wsgi_app()方法中加了這一句print(_request_ctx_stack._local.__storage__),wsgi_app()是后端接收應(yīng)用服務(wù)器發(fā)來(lái)的包裝好的WSGI請(qǐng)求的函數(shù),后面會(huì)講到,由于一個(gè)線程只能處理一個(gè)請(qǐng)求,所以結(jié)果應(yīng)該是棧中永遠(yuǎn)只有一個(gè)請(qǐng)求對(duì)象,在路由接口中我延時(shí)了1秒,假設(shè)成阻塞,看一下結(jié)果:

 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
{139851542578944: {"stack": []}}
127.0.0.1 - - [14/Apr/2018 14:31:17] "GET / HTTP/1.1" 200 -
{139851542578944: {"stack": []}}
127.0.0.1 - - [14/Apr/2018 14:31:18] "GET / HTTP/1.1" 200 -
{139851542578944: {"stack": []}}
127.0.0.1 - - [14/Apr/2018 14:31:19] "GET / HTTP/1.1" 200 -

每次棧中只有一個(gè)請(qǐng)求對(duì)象,這也就說(shuō)明了棧這個(gè)結(jié)構(gòu)和web runtime下的單線程無(wú)關(guān),那么就剩下非web runtime的情況了,最常見(jiàn)的是離線測(cè)試:

from flask import Flask,_request_ctx_stack,_app_ctx_stack


app = Flask(__name__)
app2 = Flask(__name__)


def offline_test():
    with app.app_context():
        print(_app_ctx_stack._local.__storage__)
        with app2.app_context():
            print(_app_ctx_stack._local.__storage__)

    with app.app_context():
        with app.test_request_context():
            print(_request_ctx_stack._local.__storage__)
            with app.test_request_context():
                print(_request_ctx_stack._local.__storage__)

離線測(cè)試是單線程的,通過(guò)這個(gè)例子也能得到第二的問(wèn)題的答案,為什么要將請(qǐng)求和應(yīng)用分開(kāi),一個(gè)原因是flask支持多個(gè)app共存,這需要用到中間件,另一個(gè)原因是離線測(cè)試時(shí),有可能只需要用到應(yīng)用上下文,所以需要將兩者分開(kāi),在離線測(cè)試時(shí)如果進(jìn)行了嵌套則棧結(jié)構(gòu)的特點(diǎn)就發(fā)揮了出來(lái),看一下運(yùn)行的結(jié)果:

{140402410657536: {"stack": []}}
{140402410657536: {"stack": [, ]}}
{140402410657536: {"stack": []}}
{140402410657536: {"stack": [, ]}}

結(jié)果顯而易見(jiàn)
總結(jié)一下:棧結(jié)構(gòu)和分離請(qǐng)求和應(yīng)用是為了離線測(cè)試更加靈活
web應(yīng)用服務(wù)器 WSGI 后端之間的關(guān)系
web應(yīng)用服務(wù)器的作用是監(jiān)聽(tīng)端口,當(dāng)接收到客戶端的請(qǐng)求后將請(qǐng)求轉(zhuǎn)化成WSGI格式(environ)然后傳給后端框架
應(yīng)用服務(wù)器<----WSGI協(xié)議---->后端框架
WSGI是應(yīng)用服務(wù)器和后端框架之間的橋梁,使得服務(wù)器和后端框架分離,各司其職,程序員也能專注于自己的邏輯
在WSGI中規(guī)定了每個(gè)python web應(yīng)用都需要是一個(gè)可調(diào)用的對(duì)象,即實(shí)現(xiàn)了__call__這個(gè)特殊方法,Flask就是一個(gè)可調(diào)用對(duì)象
web應(yīng)用服務(wù)器從哪里將包裝好的請(qǐng)求發(fā)送給后端
在flask中使用了werkzeug這個(gè)工具包,在werkzeug.serving中有一個(gè)類,class WSGIRequestHandler(BaseHTTPRequestHandler, object)
這個(gè)類提供了environ字典對(duì)象,定義了start_response()和run_wsgi()方法,在run_wsgi()中有一個(gè)execute(),看一下源碼:

def execute(app):
    application_iter = app(environ, start_response)  #從這里發(fā)送到后端
    try:
        for data in application_iter:
            write(data)
        if not headers_sent:
            write(b"")
    finally:
        if hasattr(application_iter, "close"):
            application_iter.close()
        application_iter = None

第一句application_iter = app(environ, start_response)就調(diào)用了Flask.__call__(),并將environ, start_response傳入,而Flask.__call__()就return了self.wsgi_app(),
這個(gè)wsgi_app(environ, start_response)是一個(gè)標(biāo)準(zhǔn)的請(qǐng)求處理函數(shù),所以它就是整個(gè)后端處理請(qǐng)求的入口函數(shù),environ是一個(gè)包含所有HTTP請(qǐng)求信息的字典對(duì)象,start_response是一個(gè)發(fā)送HTTP響應(yīng)的函數(shù),environ是從應(yīng)用服務(wù)器傳過(guò)來(lái)的,start_response是定義好的,這些都不需要后端開(kāi)發(fā)人員關(guān)心
總結(jié)一下:
1.WSGI規(guī)定了后端處理函數(shù)的格式,即需要接受environ,start_response這兩個(gè)參數(shù),這兩個(gè)參數(shù)從應(yīng)用服務(wù)器傳給后端框架
2.python web應(yīng)用對(duì)象需要是可調(diào)用的,即實(shí)現(xiàn)了__call__方法,返回WSGI規(guī)定格式的后端處理函數(shù)來(lái)處理請(qǐng)求及返回響應(yīng)
3.應(yīng)用服務(wù)器會(huì)使用werkzeug.serving中的WSGIRequestHandler類中的相應(yīng)方法,將http請(qǐng)求轉(zhuǎn)化成WSGI格式,所以說(shuō)werkzeug是一個(gè)遵循WSGI協(xié)議的工具包,提供給應(yīng)用服務(wù)器使用
后端處理請(qǐng)求返回響應(yīng)整個(gè)流程
之前說(shuō)到,后端處理請(qǐng)求的入口函數(shù)是wsgi_app(self,environ,start_response),先看下源碼:

    def wsgi_app(self, environ, start_response):
        ctx = self.request_context(environ)  #1
        ctx.push()  #2
        error = None
        try:
            try:
                response = self.full_dispatch_request()  #3
            except Exception as e:
                error = e
                response = self.handle_exception(e)
            except:
                error = sys.exc_info()[1]
                raise
            return response(environ, start_response)
        finally:
            if self.should_ignore_error(error):
                error = None
            ctx.auto_pop(error)

其中有三句比較關(guān)鍵,我寫(xiě)了序號(hào)
第一句:self.request_context(environ),看下request_context這個(gè)方法:

    def request_context(self, environ):
        return RequestContext(self, environ)

簡(jiǎn)而言之,傳入environ,初始化一個(gè)請(qǐng)求上下文對(duì)象并返回
第二句:ctx.push(),看下源碼:

    def push(self):
        top = _request_ctx_stack.top
        if top is not None and top.preserved:
            top.pop(top._preserved_exc)

        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)

        if hasattr(sys, "exc_clear"):
            sys.exc_clear()

        _request_ctx_stack.push(self)

        self.session = self.app.open_session(self.request)
        if self.session is None:
            self.session = self.app.make_null_session()

簡(jiǎn)而言之,推入應(yīng)用上下文和請(qǐng)求上下文,如果設(shè)置了secret_key則開(kāi)啟一個(gè)session,關(guān)于flask的session放到后面說(shuō)
第三句:self.full_dispatch_request(),是處理請(qǐng)求的關(guān)鍵函數(shù),看下源碼:

    def full_dispatch_request(self):
        """Dispatches the request and on top of that performs request
        pre and postprocessing as well as HTTP exception catching and
        error handling.

        .. versionadded:: 0.7
        """
        self.try_trigger_before_first_request_functions()
        try:
            request_started.send(self)
            rv = self.preprocess_request()  #function1
            if rv is None:
                rv = self.dispatch_request()  #function2
        except Exception as e:
            rv = self.handle_user_exception(e)
        return self.finalize_request(rv)  #function3

這個(gè)函數(shù)中嵌套了另外三個(gè)函數(shù),預(yù)處理函數(shù)preprocess_request(),主處理函數(shù)dispatch_request()和最終處理函數(shù)finalize_request(rv)
1.preprocess_request()是處理被before_request裝飾器裝飾的函數(shù)
2.dispatch_request()匹配請(qǐng)求的URL,并返回視圖函數(shù)的返回值rv
3.finalize_request(rv)接受視圖函數(shù)的返回值,并生成響應(yīng),這里有make_response和process_response這兩個(gè)函數(shù),make_response生成響應(yīng)對(duì)象,process_response對(duì)響應(yīng)做一些處理,比如后面要講到的session
響應(yīng)生成后,在wsgi_app中return response,最后調(diào)用ctx.auto_pop()將請(qǐng)求和應(yīng)用上下文推出棧,return的response會(huì)通過(guò)start_response發(fā)送到應(yīng)用服務(wù)器,并由其發(fā)送到客戶端,這樣一次請(qǐng)求就結(jié)束了.
最后說(shuō)說(shuō)session
flask中的session是client side session,說(shuō)白了就是session會(huì)封裝在cookie中在最終響應(yīng)時(shí)會(huì)發(fā)送給客戶端,而在服務(wù)器本地不會(huì)存儲(chǔ),所以叫作client side session,要使用session需要設(shè)置secret_key這個(gè)配置,通過(guò)app.secret_key來(lái)設(shè)置,用來(lái)驗(yàn)證簽名,等到下次客戶端發(fā)來(lái)帶有cookie的請(qǐng)求時(shí),后端就能從生成對(duì)應(yīng)的session中解析出帶有的信息,寫(xiě)個(gè)簡(jiǎn)單的應(yīng)用來(lái)看下session怎么用:

from flask import Flask,session


app = flask.Flask(__name__)
app.secret_key = "gjx"


@app.route("/")
def index():
    if "name" in session:
        print(session["name"])
    else:
        print("stranger")
    return "

/

" @app.route("/") def test(name): session["name"] = name print("session set successful") return "

test

" app.run(port=3000)

跑起來(lái)后,在瀏覽器輸入127.0.0.1:3000/,會(huì)打印出stranger,
然后訪問(wèn)127.0.0.1:3000/jx后,后端打印出session set successful,并且瀏覽器會(huì)收到服務(wù)器發(fā)來(lái)的cookie,
Set-Cookie:session=eyJuYW1lIjoiangifQ.DbNHuQ.MPZLWzoLdga2SPMg0plMYmKlJMc; HttpOnly; Path=/ 這是我測(cè)試時(shí)收到的,有三個(gè)字段,第一個(gè)是session的內(nèi)容,第二個(gè)是時(shí)間戳,第三個(gè)是驗(yàn)證信息
這時(shí)已經(jīng)設(shè)置好了session,并且得到了cookie,再次訪問(wèn)127.0.0.1:3000/,后端打印出了jx,就是之前設(shè)置的值
如果對(duì)session內(nèi)的值更改,則返回的cookie也會(huì)更改,那么在那保存,在那創(chuàng)建session呢?
之前在分析后端請(qǐng)求流程是提到了,在RequestContext的push方法最后:

        self.session = self.app.open_session(self.request)
        if self.session is None:
            self.session = self.app.make_null_session()

如果設(shè)置了secret_key則會(huì)執(zhí)行open_session開(kāi)啟一個(gè)session,那如果更改了在哪里保存呢?
在finalize_request執(zhí)行的self.process_response中:

    def process_response(self, response):
        ctx = _request_ctx_stack.top
        bp = ctx.request.blueprint
        funcs = ctx._after_request_functions
        if bp is not None and bp in self.after_request_funcs:
            funcs = chain(funcs, reversed(self.after_request_funcs[bp]))
        if None in self.after_request_funcs:
            funcs = chain(funcs, reversed(self.after_request_funcs[None]))
        for handler in funcs:
            response = handler(response)
        if not self.session_interface.is_null_session(ctx.session):
            self.save_session(ctx.session, response)
        return response

在最后判斷如果session不是null session的話會(huì)執(zhí)行self.save_session來(lái)保存更新session,在self.save_session中會(huì)調(diào)用response.set_cookie,flask中的session大概就是這樣
總結(jié)一下:
1.分析了應(yīng)用服務(wù)器封裝好的environ從哪發(fā)送給后端
2.分析了應(yīng)用服務(wù)器 WSGI 后端之間的關(guān)系以及WSGI協(xié)議對(duì)接口的標(biāo)準(zhǔn)定義,使得后端人員只需要關(guān)心自己的邏輯
3.分析了后端接收到應(yīng)用服務(wù)器發(fā)來(lái)的WSGI請(qǐng)求之后的一系列處理流程,主要函數(shù)是wsgi_app(environ,start_response)
4.最后簡(jiǎn)單分析了flask中的session機(jī)制,它是client side session的.

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/44730.html

相關(guān)文章

  • 從零開(kāi)始搭建論壇(一):Web務(wù)器Web框架

    摘要:服務(wù)器通過(guò)協(xié)議與客戶端通信,因此也被稱為服務(wù)器。本文標(biāo)題為從零開(kāi)始搭建論壇一服務(wù)器與框架本文鏈接為更多閱讀自己動(dòng)手開(kāi)發(fā)網(wǎng)絡(luò)服務(wù)器一自己動(dòng)手開(kāi)發(fā)網(wǎng)絡(luò)服務(wù)器二自己動(dòng)手開(kāi)發(fā)網(wǎng)絡(luò)服務(wù)器三服務(wù)器網(wǎng)關(guān)接口實(shí)現(xiàn)原理分析最佳實(shí)踐指南應(yīng)用淺談框架編程簡(jiǎn)介 之前用 Django 做過(guò)一個(gè)小的站點(diǎn),感覺(jué)Django太過(guò)笨重,于是就準(zhǔn)備換一個(gè)比較輕量級(jí)的 Web 框架來(lái)玩玩。Web.py 作者已經(jīng)掛掉,項(xiàng)目好...

    dantezhao 評(píng)論0 收藏0
  • Python Web開(kāi)發(fā)最難懂WSGI協(xié)議,到底包含哪些內(nèi)容?

    摘要:通過(guò)回調(diào)函數(shù)將響應(yīng)狀態(tài)和響應(yīng)頭返回給,同時(shí)返回響應(yīng)正文,響應(yīng)正文是可迭代的并包含了多個(gè)字符串。返回響應(yīng)正文負(fù)責(zé)獲取請(qǐng)求,將請(qǐng)求傳遞給,由處理請(qǐng)求后返回。 我想大部分Python開(kāi)發(fā)者最先接觸到的方向是WEB方向(因?yàn)榭偸怯虚_(kāi)發(fā)者希望馬上給自己做個(gè)博客出來(lái),例如我),既然是WEB,免不了接觸到一些WEB框架,例如Django,Flask,Torando等等,在開(kāi)發(fā)過(guò)程中,看過(guò)一些文檔總會(huì)...

    atinosun 評(píng)論0 收藏0
  • 如何理解Nginx, WSGI, Flask之間關(guān)系

    摘要:通過(guò)查閱了些資料,總算把它們的關(guān)系理清了。在這個(gè)過(guò)程中,服務(wù)器的作用是接收請(qǐng)求處理請(qǐng)求返回響應(yīng)服務(wù)器是一類特殊的服務(wù)器,其作用是主要是接收請(qǐng)求并返回響應(yīng)。正是為了替代而出現(xiàn)的。三結(jié)語(yǔ)最后以,,之間的對(duì)話結(jié)束本文。 剛轉(zhuǎn)行互聯(lián)網(wǎng)行業(yè),聽(tīng)到了許多名詞:Flask、Django、WSGI、 Nginx、Apache等等,一直無(wú)法搞清楚這些開(kāi)源項(xiàng)目之間的關(guān)系,直至看到這篇文章后感覺(jué)醍醐灌頂,以...

    魏明 評(píng)論0 收藏0
  • 從零開(kāi)始搭建論壇(二):Web務(wù)器網(wǎng)關(guān)接口

    摘要:在從零開(kāi)始搭建論壇一服務(wù)器與框架中我們弄清楚了服務(wù)器應(yīng)用程序框架的概念。框架應(yīng)用生成狀態(tài)碼以及響應(yīng)報(bào)頭,然后將二者傳遞至,等待服務(wù)器保存。添加響應(yīng)頭,狀態(tài)碼返回響應(yīng)信息創(chuàng)建一個(gè)服務(wù)器實(shí)例目前支持的成熟服務(wù)器有很多,是相當(dāng)不錯(cuò)的一個(gè)。 在 從零開(kāi)始搭建論壇(一):Web服務(wù)器與Web框架 中我們弄清楚了Web 服務(wù)器、Web 應(yīng)用程序、Web框架的概念。對(duì)于 Python 來(lái)說(shuō),越來(lái)越多...

    Astrian 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<