Flask源碼閱讀筆記(二)

從app.run()到@app.route發生了什麼?

簡而言之,app.run()啟動了flask自己實現的一個web伺服器,創建socket監聽伺服器埠,對接收到的客戶端request進行解析,然後根據之前註冊的路由把合法的請求內容傳遞給對應的路由處理函數,再從路由處理函數取得響應,加工成合法的http報文後返回給客戶端程序。

還是以這個最小代碼為例,如下:

from flask import Flasknnapp = Flask(__name__)nnn@app.route(/index)ndef index():n return "<h1>hello world!</h1>"nnif __name__ == "__main__":n app.run()n

app.run()在腳本啟動的時候開始執行,這個函數裡面進行配置後調用了Werkzeug的方法然後啟動了一個http server。配置主要為監聽的IP地址和埠,如果沒有通過入參傳入就啟用默認的127.0.0.1:5000配置進行監聽。啟動http server的時候創建了一個socket,用於監聽接收客戶端請求。當server的各種初始化和配置技術後,flask啟動了serve_forever()方法,其實這就是一個循環,不斷的從監聽socket中取得request。

創建的http server時的主要工作除了創建一個socket監聽埠外,還有綁定一個request handler。這個request handler將在server得到request的時候實例化,並按照http報文的格式將request請求分解,集合成environment交給app,按照path名字route rule交給對應的路由處理函數進行數據處理和響應。此外,http sever里還會對創建它的app進行反向指向,同時,request handler中也會回指創建它的server實例,並從server實例中取得app,如此,信息完成了一個從app到server到request handler再回到app的閉環線路。

上面這部分內容較為簡單,之前接觸過tcp/ip編程的話就不能理解。下面主要介紹一下這個閉環是如何指導request handler把請求交給對應的路由處理函數的。

這裡涉及到的最關鍵的函數是request handler(具體是WSGIRequestHandler類)的run_wsgi(),這裡簡要介紹一下wsgi。

所謂wsgi,可以認為是一種web伺服器程序和web應用程序間的約定或者規定,它規定了伺服器程序和應用程序間數據傳遞的規則,當然這是一大塊內容,我目前也沒有全部研讀過這些標準,但是簡單來說,從web應用開發者的角度,它主要規定了web應用程序應當接受兩個參數,一個是字典environment,一個是可調用的用來發送HTTP響應的函數,那麼這個符合wsgi的應用程序(或者函數)應當長這個樣子:

def app(environment,start_response)n

這個程序應當被web server程序調用,並傳入兩個入參。

回到flask源碼中看,這個體現wsgi規則的地方就是上面提到的run_wsgi()方法。這個方法還是挺長的,忘了提了,為了便於閱讀理解,目前我看的都是0.10版本的flask源碼。這個方法的前半部分就是解析http報文,把字元串形式的request信息,生成一個字典,也就是應用程序需要的那個environment入參;然後後半部分就是調用應用程序生成響應內容。注意看下面這段代碼:

app的調用就是傳入兩個參數,和之前舉的例子是不是一樣?

(網站的網頁是很多的,當然,雖然會很難以維護和理解,完全可以把所有網頁內容響應內容都放在一個函數中實現,無非就是在函數中多做幾個判斷,區分客戶端不同的請求內容。但這樣做效率太低,所以才有使用web框架的意義。框架把大部分請求都會用到的部分提煉總結,變成一塊公用代碼,開發者只需要把每一個path對應的處理函數寫好,註冊到框架里,請求就可以完美處理了。這篇文字到上一段為止講述的其實是flask自帶的一個web伺服器的功能,而flask本身作為web框架的工作其實下面才開始涉及。)

於是一個flask實例在這麼一大圈後終於從request handler手中接過了接力棒,拿到了解析生成的environment信息,

k=wsgi.multiprocess v=Falsenk=SERVER_SOFTWARE v=Werkzeug/0.11.10nk=SCRIPT_NAME v=nk=REQUEST_METHOD v=GETnk=PATH_INFO v=/indexnk=SERVER_PROTOCOL v=HTTP/1.1nk=QUERY_STRING v=nk=werkzeug.server.shutdown v=<function shutdown_server at 0x00000000036F8898>nk=CONTENT_LENGTH v=nk=HTTP_USER_AGENT v=Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36nk=HTTP_CONNECTION v=keep-alivenk=SERVER_NAME v=127.0.0.1nk=REMOTE_PORT v=54515nk=wsgi.url_scheme v=httpnk=SERVER_PORT v=5000nk=wsgi.input v=<socket._fileobject object at 0x00000000037112A0>nk=HTTP_HOST v=localhost:5000nk=wsgi.multithread v=Falsenk=HTTP_UPGRADE_INSECURE_REQUESTS v=1nk=HTTP_CACHE_CONTROL v=max-age=0nk=HTTP_ACCEPT v=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8nk=wsgi.version v=(1, 0)nk=wsgi.run_once v=Falsenk=wsgi.errors v=<open file <stderr>, mode w at 0x000000000276B150>nk=REMOTE_ADDR v=127.0.0.1nk=HTTP_ACCEPT_LANGUAGE v=zh-CN,zh;q=0.8nk=CONTENT_TYPE v=nk=HTTP_ACCEPT_ENCODING v=gzip, deflate, sdchn

打出來看一下,發現environment里內容還真不少。

再接下來app會根據路由規則調用對應path的view function,產生response。這段內容涉及到上下文,是flask中比較關鍵的一部分內容。我繼續看代碼去咯。

推薦閱讀:

php7 更新後對於 python 會造成怎樣的衝擊?python+c 擴展能否 hold 住性能?
請問開發的混合應用Hybrid App可以和Native應用「混合」成一個App嗎?
JS的基本數據類型的臨時包裝類型對象的觸發條件和生命周期是多久?下面代碼為何結果迥異?
為什麼 Tornado 在調用 self.finish() 以後不終止 RequestHandler 中相關處理函數的運行?
有哪些python+flask的搭建的博客或論壇開源推薦?

TAG:Python | Flask | Web开发 |