摘要:另外,如果你對模板渲染部分的內容感興趣,也可以考慮閱讀文檔文檔文檔源碼閱讀,可以參考下面的函數(shù)打斷點,再測試一個請求,理清過程。
Flask-Origin 源碼版本
一直想好好理一下flask的實現(xiàn),這個項目有Flask 0.1版本源碼并加了注解,挺清晰明了的,我在其基礎上完成了對Werkzeug的理解部分,大家如果想深入學習的話,可以參考werkzeug_flow.md.
閱讀前為了更容易理解Flask的實現(xiàn)原理,你需要對WSGI協(xié)議以及HTTP協(xié)議有一些了解,建議先簡單瀏覽下面的基本知識:
PEP 0333和PEP 3333(WSGI實現(xiàn))
HTTP概述
Flask內部實現(xiàn)大量依賴于Werkzeug,包括請求和響應對象,路由匹配,URL生成等等,你可以閱讀Werkzeug的文檔來深入了解這些內容的具體實現(xiàn)。另外,如果你對模板渲染部分的內容感興趣,也可以考慮閱讀Jinja2文檔:
Werkzeug文檔
Jinja2文檔
werkzeug源碼閱讀,可以參考下面的函數(shù)打斷點,再測試一個請求,理清過程。其實可以參考簡化后web服務實現(xiàn)思路,socket建立后,監(jiān)聽recv到的請求信息(no_wsgi_dome.no_wsgi.Socket._handle)并解析,然后調用相應的app.route對應的view_func.整個過程可以大致分為兩部分: 1. app-> werkzeug-> http-> socket 啟動端口監(jiān)聽,注冊各種方法. 2. socket recv到請求-> 初始化RequestHandlerClass-> 調用Flask.__call__, wsgi_app在請求上下文中執(zhí)行預處理方法,視圖方法,后響應方法等.flask啟動流程,只追溯了app到http再到socket的啟動,主流程就是BaseWSGIServer初始化調用了HTTPServer的初始化,進而初始化了BaseServer,在socketserver上啟動了服務開始監(jiān)聽端口:
flask.Flask.run -> werkzeug.serving.run_simple -> werkzeug.serving.run_simple.inner ->werkzeug.serving.make_server -> BaseWSGIServer->HTTPServer.__init__(self, get_sockaddr(host, int(port),self.address_family), handler) -> BaseServer.__init__(self, server_address, RequestHandlerClass) -> werkzeug.serving.run_simple.inner.srv.serve_forever() -> socketserver.BaseServer.serve_forever 建立socket服務開始監(jiān)聽,當ready也就是有recv到請求時開始 _handle_request_noblockflask處理請求流程,追溯了socket接受到請求后觸發(fā)app處理請求的主流程:
curl發(fā)出請求 -> socket接受到請求 -> SocketServer.BaseServer.serve_forever._handle_request_noblock -> SocketServer.BaseServer.process_request -> SocketServer.BaseServer.finish_request -> socketserver.BaseServer.__init__:self.RequestHandlerClass(request, client_address, self) -> 這里要找出RequestHandlerClass是如何初始化的,它的真身是什么 -> socketserver.TCPServer.__init__:BaseServer.__init__(self, server_address, RequestHandlerClass) -> http.server.HTTPServer(未重寫__init__) -> werkzeug.serving.BaseWSGIServer:HTTPServer.__init__(self, get_sockaddr(host, int(port),self.address_family), handler) (此處handler就是WSGIRequestHandler) -> RequestHandlerClass的真身已經找到,就是WSGIRequestHandler 也就是說每次請求來了都初始化一個WSGIRequestHandler去處理 -> 處理的入口應該是werkzeug.serving.WSGIRequestHandler.handle可是簡單一看并沒找到是如何開始處理請求的-> 往它的父類BaseHTTPRequestHandler中找也沒有 -> 再往上socketserver.StreamRequestHandler -> 找到了SocketServer.BaseRequestHandler.__init__:try:self.handle() 關鍵點 -> 開始調用子類WSGIRequestHandler中的handle方法 -> werkzeug.serving.WSGIRequestHandler (注意handler和handle_one_request,WSGIRequestHandler重載了BaseHTTPServer.BaseHTTPRequestHandler中的方法,BaseHTTPRequestHandler由重載了 SocketServer.BaseRequestHandler )-> werkzeug.serving.WSGIRequestHandler.handle_one_request調用werkzeug.serving.WSGIRequestHandler.run_wsgi 開始處理請求 -> run_wsgi.execute(self.server.app)將請求交予app來處理 -> flask.Flask.__call__ -> flask.Flask.wsgi_app 開始app內的流程,交由wsgi_app在請求上下文中執(zhí)行預處理方法,視圖方法,后響應方法等。可以看到實現(xiàn)過程中Server,Handler用到了繼承并重載,層層包裝了web服務
BaseWSGIServer繼承了HTTPServer重寫了BaseServer.serve_forever(包了一層), HTTPServer繼承了TCPServer重寫了server_bind, TCPServer繼承了BaseServer重寫了server_bind, 主要思路要理清socket接受接請求后如何用請求觸發(fā)調用app,這里主要是SocketServer.BaseRequestHandler.__init__:try:self.handle() 這個__init__才是處理請求真正開始的地方.進一步
web的最原始的實現(xiàn)見 no_wsgi_dome ,不使用werkzeug,不使用wsgi約束,只是用socket如何實現(xiàn)http服務.這個對理解wsgi對http以及socket的封裝有很好的借鑒意義.
補充了本地上下文相關的本地線程、本地堆棧、本地代理,并寫了個LocalProxy_dome.py 輔助理解flask中是如何使用LocalProxy的。
我通過打斷點,理通了app的啟動和接受請求到處理請求的過程,可以參考werkzeug_flow.md配合flask_dome.py并手動打斷點嘗試一下.
根據(jù)下面的提示,自己理一下吧.
Flask中的請求響應循環(huán)
路由系統(tǒng)
本地上下文
請求與響應對象
session
藍本
模板渲染
最后,要是覺得不錯的話,點個贊支持一下吧,相關源碼都放到了 我的github.文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/43535.html
摘要:今天對象在學習時發(fā)現(xiàn)對象的方法并不能清理一級緩存同一下相同查詢條件返回的結果還是舊值。測試代碼如下上網搜索網上搜索找到了相同問題并沒有人解答。例如查看官方文檔實例有一個本地緩存在執(zhí)行和時被清理。要明確地關閉它獲取打算做更多的工作你可以調用。 今天對象在學習 Mybatis 時發(fā)現(xiàn) org.apache.ibatis.session.SqlSession 對象的 clearCache()...
摘要:源碼走讀在運行時發(fā)生了什么標簽空格分隔原文作者是,原文地址是在這篇博文中我將回答一下問題運行一個期間內部發(fā)生了什么開始首先從并檢查代碼。這個發(fā)生在和行之間。如果一個錯誤發(fā)生,它會被記錄,然后程序退出。這些覆蓋了在客戶端里面發(fā)生了什么。 Docker 源碼走讀 - 在運行 Docker run 時發(fā)生了什么? 標簽(空格分隔): Docker 原文作者是 Frank Scho...
摘要:攔截器注冊配置攔截器注冊配置通過可以發(fā)現(xiàn)中的的清理工作沒有得到執(zhí)行。只會在出現(xiàn)異常,返回或正常執(zhí)行結束才會從索引依次往前執(zhí)行。 發(fā)現(xiàn)問題 最近在進行壓測發(fā)現(xiàn),有一些接口時好時壞,通過sentry日志平臺及sky walking平臺跟蹤發(fā)現(xiàn),用戶張三獲取到的用戶上下文確是李四。 代碼走讀 用戶登錄下上文 /** * 用戶登錄下上文 * * @author : jamesfu * @da...
摘要:如果當前不是主線程則直接調用,如果是線程則創(chuàng)建一個加入到后臺的一個隊列,最終由中的一個線程池去調用。拋出線程狀態(tài)非法異常。 while (clazz != null) {String name = clazz.getName();if (name.startsWith(java.) || name.starts...
閱讀 1919·2021-09-23 11:21
閱讀 1704·2019-08-29 17:27
閱讀 1062·2019-08-29 17:03
閱讀 729·2019-08-29 15:07
閱讀 1927·2019-08-29 11:13
閱讀 2385·2019-08-26 12:14
閱讀 931·2019-08-26 11:52
閱讀 1736·2019-08-23 17:09