Flask源碼分析一:服務啓動

前言

Flask是目前爲止我最喜歡的一個Python Web框架了,爲了更好的掌握其內部實現機制,這兩天準備學習下Flask的源碼,將由淺入深跟你們分享下,其中Flask版本爲1.1.1。html

Flask系列文章:python

  1. Flask開發初探

正文

本文將結合源碼跟蹤看下Flask是如何啓動並運行一個服務的。在0.11版本之後,支持命令行啓動flask。shell

目前共有兩種方式能夠載入應用:flask

1. python app.py

首先,繼續貼上最簡單的應用app.py:服務器

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello Flask!'

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

執行python app.py便可啓動。app

咱們看到,這段代碼先初始化了Flask類並被app所指向,而後執行run()來啓動程序的。框架

查看run方法:socket

def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
        if os.environ.get("FLASK_RUN_FROM_CLI") == "true":
            from .debughelpers import explain_ignored_app_run

            explain_ignored_app_run()
            return

        if get_load_dotenv(load_dotenv):
            cli.load_dotenv()

            # if set, let env vars override previous values
            if "FLASK_ENV" in os.environ:
                self.env = get_env()
                self.debug = get_debug_flag()
            elif "FLASK_DEBUG" in os.environ:
                self.debug = get_debug_flag()

        # debug passed to method overrides all other sources
        if debug is not None:
            self.debug = bool(debug)

        _host = "127.0.0.1"
        _port = 5000
        server_name = self.config.get("SERVER_NAME")
        sn_host, sn_port = None, None

        if server_name:
            sn_host, _, sn_port = server_name.partition(":")

        host = host or sn_host or _host
        # pick the first value that's not None (0 is allowed)
        port = int(next((p for p in (port, sn_port) if p is not None), _port))

        options.setdefault("use_reloader", self.debug)
        options.setdefault("use_debugger", self.debug)
        options.setdefault("threaded", True)

        cli.show_server_banner(self.env, self.debug, self.name, False)

        from werkzeug.serving import run_simple

        try:
            run_simple(host, port, self, **options)
        finally:
            # reset the first request information if the development server
            # reset normally.  This makes it possible to restart the server
            # without reloader and that stuff from an interactive shell.
            self._got_first_request = False

首先入參:ide

參數 說明
host 服務器地址,不設置的話默認爲127.0.0.1
port 端口,不設置的話默認爲5000
debug 是否爲調試模式, 默認爲否
load_dotenv 從項目根目錄下的.flaskenv.env文件中導入環境變量

該方法的處理流程是:對入參進行配置處理以後,執行werkzeug的run_simple()方法,學習

run_simple將啓動一個WSGI服務。

關於WSGI協議:

  1. 它是關於HTTP服務器和Web應用的橋樑,定義了標準接口以提高Web應用之間的可移植性,是一套接口交互規範。
  2. 它的功能是監聽指定端口服務,未來自HTTP服務器的請求解析爲WSGI格式,調用Flask app處理請求。

run_simple中的inner方法是核心,inner調用make_server().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)

make_server會根據線程或者進程數返回相應的WSGI服務器,默認狀況下返回BaseWSGIServer,ThreadedWSGIServer和ForkingWSGIServer均集成了BaserWSGIServer,接下來咱們看下serve_forever()方法:

def serve_forever(self):
    self.shutdown_signal = False
    try:
        HTTPServer.serve_forever(self)
    except KeyboardInterrupt:
        pass
    finally:
        self.server_close()

最終調用了Python標準類庫接口HTTPServer的serve_forever()方法,而HTTPServer又是socketserver.TCPServer的子類,經過server_bind來監聽服務:

class HTTPServer(socketserver.TCPServer):

    allow_reuse_address = 1    # Seems to make sense in testing environment

    def server_bind(self):
        """Override server_bind to store the server name."""
        socketserver.TCPServer.server_bind(self)
        host, port = self.server_address[:2]
        self.server_name = socket.getfqdn(host)
        self.server_port = port

2. Flask命令

接下來咱們經過flask命令來啓動一個應用,hello.py:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello Flask!'

Unix Bash ( Linux 、Mac 及其餘):

$ export FLASK_APP=hello
$ flask run

這樣便啓動了該 應用,那麼內部的實現機理是怎樣的呢?

  1. 設置環境變量Flask_APP,指定應用的路徑
  2. 經過run命令來啓動開發服務器,其中flask命令是由Flask安裝的。

以上,就是Flask服務啓動的流程。

相關文章
相關標籤/搜索