Flask 源碼剖析——服務啟動篇

【Flask官方文檔經典示例】 hello.py

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()

輸入以下命令啟動應用程序:

$ python hello.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

打開你的瀏覽器并在地址欄輸入http://127.0.0.1:5000/ ?!緢D1-1】顯示連接到應用程序后的瀏覽器。

圖1-1

圖1-1 hello.py Flask應用程序

服務是怎么啟動的

app.run()開始,這行代碼表示啟動一個服務。我們看到appFlask一個對象,而run()是該對象的一個方法。我們先簡單的認為定義了一個類,然后實例化這個類并調用該類的一個方法,如下:

【示例1-1】example-1-1.py

class Flask(object):
    def run(self):
        pass

app = Flask()
app.run()

如果我們運行【示例1-1】這段代碼,會發(fā)現什么都沒有發(fā)生。然而,【Flask官方文檔經典示例】不是這樣的,當你運行后它是下面這樣的:

$ python hello.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

很自然的可以想到,【Flask官方文檔經典示例】中的app.run()不簡單,我們可以看看run()方法定義,如下:

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()函數,發(fā)現該函數是從werkzeug.serving模塊中導入的。

到了這里我們就不得不提一下Werkzeug了,官方文檔定義:Werkzeug是為Python設計的HTTPWSGI實用程序庫。至于它有什么作用,我們在這里暫且不討論,先跟到代碼里面看看它都做了什么。

我們看到這個run_simple()函數里面還嵌套了一個inner()函數,里面有幾行關鍵代碼,如下:

srv = make_server(hostname, port, application, threaded, 
                  processes, request_handler, 
                  passthrough_errors, ssl_context, 
                  fd=fd)
...
srv.serve_forever()

從上面的代碼,我們看到在inner()函數里面調用了make_server()函數來創(chuàng)建一個類實例,該實例會調用serve_forever()方法讓服務一直運行,等待客戶端的請求。到這里我們大概找到了服務啟動的入口了,想知道具體是怎么啟動,我們還需要深入挖掘一下。

因為調用run_simple()函數時參數threadedprocesses給的都是默認值,分別為False1,所以在這里make_server()函數其實是創(chuàng)建了一個BaseWSGIServer類實例,并調用該實例的serve_forever()方法,具體make_server()函數如下:

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類,如下代碼:

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()方法,然后我們發(fā)現BaseWSGIServer類繼承了HTTPServer類,且BaseWSGIServerserve_forever()方法中調用了HTTPServerserve_forever()方法。找到HTTPServer類,如下代碼:

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里面看看,如下代碼:

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輪詢的時間。然后進入一個無限循環(huán),調用select方式進行網絡IO監(jiān)聽。也就是說app.run()啟動的是一個BaseWSGIServer,該服務通過一層一層的繼承創(chuàng)建socket來進行網絡監(jiān)聽,等待客戶端連接。

至此,Flask服務是怎么啟動的應該有個基本的了解了。

整理一下相關server類的繼承關系,如下:
BaseWSGIServer-->HTTPServer-->SocketServer.TCPServer-->BaseServer
從上面的類繼承關系,我們可以很容易的理解,因為Flask是一個Web框架,所以需要一個HTTP服務,而HTTP服務是基于TCP服務的,而TCP服務最終會有一個基礎服務來處理socket。這一條線都能夠解釋的通。但是,那個BaseWSGIServer是個什么鬼?為什么會需要一層這個服務?這也是我想要去研究的,所以我會在下一篇里面進行講解。

原文:

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容