【Flask官方文件經典示例】 hello.py
1 2 3 4 5 6 7 8 9 |
from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World!" if __name__ == "__main__": app.run() |
輸入以下命令啟動應用程式:
1 2 |
$ python hello.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) |
開啟你的瀏覽器並在位址列輸入http://127.0.0.1:5000/ 。【圖1-1】顯示連線到應用程式後的瀏覽器。
圖1-1 hello.py Flask應用程式
服務是怎麼啟動的
從app.run()
開始,這行程式碼表示啟動一個服務。我們看到app
是Flask
一個物件,而run()
是該物件的一個方法。我們先簡單的認為定義了一個類,然後例項化這個類並呼叫該類的一個方法,如下:
【示例1-1】example-1-1.py
1 2 3 4 5 6 |
class Flask(object): def run(self): pass app = Flask() app.run() |
如果我們執行【示例1-1】這段程式碼,會發現什麼都沒有發生。然而,【Flask官方文件經典示例】不是這樣的,當你執行後它是下面這樣的:
1 2 |
$ python hello.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) |
很自然的可以想到,【Flask官方文件經典示例】中的app.run()
不簡單,我們可以看看run()
方法定義,如下:
1 2 3 4 5 6 7 |
def run(self, host=None, port=None, debug=None, **options): from werkzeug.serving import run_simple ... try: run_simple(host, port, self, **options) finally: self._got_first_request = False |
在這個方法中,我們先忽略一些配置操作,重點關注run_simple()
函式,發現該函式是從werkzeug.serving
模組中匯入的。
到了這裡我們就不得不提一下Werkzeug了,官方文件定義:Werkzeug是為Python設計的HTTP和WSGI實用程式庫。至於它有什麼作用,我們在這裡暫且不討論,先跟到程式碼裡面看看它都做了什麼。
我們看到這個run_simple()
函式裡面還巢狀了一個inner()
函式,裡面有幾行關鍵程式碼,如下:
1 2 3 4 5 6 |
srv = make_server(hostname, port, application, threaded, processes, request_handler, passthrough_errors, ssl_context, fd=fd) ...
srv.serve_forever() |
從上面的程式碼,我們看到在inner()
函式裡面呼叫了make_server()
函式來建立一個類例項,該例項會呼叫serve_forever()
方法讓服務一直執行,等待客戶端的請求。到這裡我們大概找到了服務啟動的入口了,想知道具體是怎麼啟動,我們還需要深入挖掘一下。
因為呼叫run_simple()
函式時引數threaded
和processes
給的都是預設值,分別為False
和1
,所以在這裡make_server()
函式其實是建立了一個BaseWSGIServer
類例項,並呼叫該例項的serve_forever()
方法,具體make_server()
函式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def make_server(host=None, port=None, app=None, threaded=False, processes=1, request_handler=None, passthrough_errors=False, ssl_context=None, fd=None): if threaded and processes > 1: raise ValueError("cannot have a multithreaded and " "multi process server.") elif threaded: return ThreadedWSGIServer(host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd) elif processes > 1: return ForkingWSGIServer(host, port, app, processes, request_handler, passthrough_errors, ssl_context, fd=fd) else: return BaseWSGIServer(host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd) |
找到BaseWSGIServer
類,如下程式碼:
1 2 3 4 5 6 7 8 9 10 11 |
class BaseWSGIServer(HTTPServer, object): ... def serve_forever(self): self.shutdown_signal = False try: HTTPServer.serve_forever(self) except KeyboardInterrupt: pass finally: self.server_close() ... |
【注意】接下來的程式碼巢狀呼叫比較多,所以最好是能對照著原始碼來看。
srv.serve_forever()
其實是BaseWSGIServer
類中的serve_forever()
方法,然後我們發現BaseWSGIServer
類繼承了HTTPServer
類,且BaseWSGIServer
的serve_forever()
方法中呼叫了HTTPServer
的serve_forever()
方法。找到HTTPServer
類,如下程式碼:
1 2 3 4 5 6 7 |
class HTTPServer(SocketServer.TCPServer): allow_reuse_address = 1 def server_bind(self): SocketServer.TCPServer.server_bind(self) host, port = self.socket.getsockname()[:2] self.server_name = socket.getfqdn(host) self.server_port = port |
HTTPServer
類中並沒有serve_forever()
方法,且這個類繼承了 SocketServer.TCPServer
,我們再找到TCPServer
類,然而它也沒有serve_forever()
方法,且這個類繼承了BaseServer
類,所以再去BaseServer
裡面看看,如下程式碼:
1 2 3 4 5 6 7 8 9 10 |
def serve_forever(self, poll_interval=0.5): self.__is_shut_down.clear() try: while not self.__shutdown_request: r, w, e = _eintr_retry(select.select, [self], [], [], poll_interval) if self in r: self._handle_request_noblock() finally: self.__shutdown_request = False self.__is_shut_down.set() |
所以前面看到的srv.serve_forever()
其實是呼叫了BaseServer
裡面的serve_forever()
方法,它接受一個引數poll_interval
,用於表示select輪詢的時間。然後進入一個無限迴圈,呼叫select方式進行網路IO監聽。也就是說app.run()
啟動的是一個BaseWSGIServer
,該服務通過一層一層的繼承建立socket來進行網路監聽,等待客戶端連線。
至此,Flask服務是怎麼啟動的應該有個基本的瞭解了。
整理一下相關server類的繼承關係,如下:
BaseWSGIServer
–>HTTPServer
–>SocketServer.TCPServer
–>BaseServer
從上面的類繼承關係,我們可以很容易的理解,因為Flask是一個Web框架,所以需要一個HTTP服務,而HTTP服務是基於TCP服務的,而TCP服務最終會有一個基礎服務來處理socket。這一條線都能夠解釋的通。但是,那個BaseWSGIServer
是個什麼鬼?為什麼會需要一層這個服務?這也是我想要去研究的,所以我會在下一篇裡面進行講解。