Flask源码分析一:服务启动

  • 2019 年 10 月 22 日
  • 笔记

前言

Flask是目前为止我最喜欢的一个Python Web框架了,为了更好的掌握其内部实现机制,这两天准备学习下Flask的源码,将由浅入深跟大家分享下,其中Flask版本为1.1.1。

Flask系列文章:

  1. Flask开发初探

正文

本文将结合源码跟踪看下Flask是如何启动并运行一个服务的。

首先,继续贴上最简单的应用:

from flask import Flask  app = Flask(__name__)    @app.route('/')  def hello_world():      return 'Hello Flask!'    if __name__ == '__main__':      app.run()

我们看到,这段代码先初始化了Flask类并被app所指向,然后执行run()来启动程序的。

查看run方法:

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

首先入参:

参数 说明
host 服务器地址,不设置的话默认为127.0.0.1
port 端口,不设置的话默认为5000
debug 是否为调试模式, 默认为否
load_dotenv 设置环境变量
options

该方法的处理流程是:对入参进行配置处理之后,执行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

以上,就是Flask服务启动的流程。