zoukankan      html  css  js  c++  java
  • flask 源码专题(一):app.run()的背后

    当我们用Flask写好一个app后, 运行app.run()表示监听指定的端口, 对收到的request运行app生成response并返回. 现在分析一下, 运行app.run()后具体发生了什么事情

    Flask定义的run方法如下:

        def run(self, host=None, port=None, debug=None, **options):
            """
    .........
    """ from werkzeug.serving import run_simple if host is None: host = '127.0.0.1' if port is None: server_name = self.config['SERVER_NAME'] if server_name and ':' in server_name: port = int(server_name.rsplit(':', 1)[1]) else: port = 5000 if debug is not None: self.debug = bool(debug) options.setdefault('use_reloader', self.debug) options.setdefault('use_debugger', self.debug) 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

    在上面的源码中, from werkzeug.serving import run_simple引入run_simple函数,  该函数将定义好的app在指定的host, port上运行,run_simple函数如下: 

    def run_simple(
        hostname,
        port,
        application,
        use_reloader=False,
        use_debugger=False,
        use_evalex=True,
        extra_files=None,
        reloader_interval=1,
        reloader_type="auto",
        threaded=False,
        processes=1,
        request_handler=None,
        static_files=None,
        passthrough_errors=False,
        ssl_context=None,
    ):
        """
    .....................
        """def inner():
            try:
                fd = int(os.environ["WERKZEUG_SERVER_FD"])
            except (LookupError, ValueError):
                fd = None
            srv = make_server(
                hostname,
                port,
                application,
                threaded,
                processes,
                request_handler,
                passthrough_errors,
                ssl_context,
                fd=fd,
            )#第二步
    if fd is None: log_startup(srv.socket) srv.serve_forever() #第三步

      if use_reloader: # If we're not running already in the subprocess that is the # reloader we want to open up a socket early to make sure the # port is actually available. if not is_running_from_reloader(): if port == 0 and not can_open_by_fd: raise ValueError( "Cannot bind to a random port with enabled " "reloader if the Python interpreter does " "not support socket opening by fd." ) # Create and destroy a socket so that any exceptions are # raised before we spawn a separate Python interpreter and # lose this ability. address_family = select_address_family(hostname, port) server_address = get_sockaddr(hostname, port, address_family) s = socket.socket(address_family, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(server_address) if hasattr(s, "set_inheritable"): s.set_inheritable(True) # If we can open the socket by file descriptor, then we can just # reuse this one and our socket will survive the restarts. if can_open_by_fd: os.environ["WERKZEUG_SERVER_FD"] = str(s.fileno()) s.listen(LISTEN_QUEUE) log_startup(s) else: s.close() if address_family == af_unix: _log("info", "Unlinking %s" % server_address) os.unlink(server_address) # Do not use relative imports, otherwise "python -m werkzeug.serving" # breaks. from ._reloader import run_with_reloader run_with_reloader(inner, extra_files, reloader_interval, reloader_type) else: inner() # 第一步

    run_simple函数一般情况下调用内部定义的inner函数. inner调用make_server创建server实例srv, 然后调用srv.serve_forever.

    make_server函数如下:

    make_server函数返回BaseWSGIServer实例

    def make_server(
        host=None,
        port=None,
        app=None,
        threaded=False,
        processes=1,
        request_handler=None,
        passthrough_errors=False,
        ssl_context=None,
        fd=None,
    ):
        """Create a new server instance that is either threaded, or forks
        or just processes one request after another.
        """
        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):
    
        """Simple single-threaded, single-process WSGI server."""
    
        multithread = False
        multiprocess = False
        request_queue_size = LISTEN_QUEUE
    
        def __init__(
            self,
            host,
            port,
            app,
            handler=None,
            passthrough_errors=False,
            ssl_context=None,
            fd=None,
        ):
            if handler is None:
                handler = WSGIRequestHandler #第一步 指定BaseServer中的RequestHandlerClass作为处理请求的handler
    
            self.address_family = select_address_family(host, port)
    
            if fd is not None:
                real_sock = socket.fromfd(fd, self.address_family, socket.SOCK_STREAM)
                port = 0
    
            server_address = get_sockaddr(host, int(port), self.address_family)
    
            # remove socket file if it already exists
            if self.address_family == af_unix and os.path.exists(server_address):
                os.unlink(server_address)
            HTTPServer.__init__(self, server_address, handler)#第二步 初始化生成HTTPServer实例
    
            self.app = app
            self.passthrough_errors = passthrough_errors
            self.shutdown_signal = False
            self.host = host
            self.port = self.socket.getsockname()[1]
    
            # Patch in the original socket.
            if fd is not None:
                self.socket.close()
                self.socket = real_sock
                self.server_address = self.socket.getsockname()
    
            if ssl_context is not None:
                if isinstance(ssl_context, tuple):
                    ssl_context = load_ssl_context(*ssl_context)
                if ssl_context == "adhoc":
                    ssl_context = generate_adhoc_ssl_context()
    
                # If we are on Python 2 the return value from socket.fromfd
                # is an internal socket object but what we need for ssl wrap
                # is the wrapper around it :(
                sock = self.socket
                if PY2 and not isinstance(sock, socket.socket):
                    sock = socket.socket(sock.family, sock.type, sock.proto, sock)
                self.socket = ssl_context.wrap_socket(sock, server_side=True)
                self.ssl_context = ssl_context
            else:
                self.ssl_context = None
    
        def log(self, type, message, *args):
            _log(type, message, *args)
    
        def serve_forever(self):#第三步
            self.shutdown_signal = False
            try:
                HTTPServer.serve_forever(self)#第四步调用serve_forever
         except KeyboardInterrupt: pass finally: self.server_close()

    BaseWSGIServer 类中,指定BaseServer中的RequestHandlerClass作为处理请求的handler

    HTTPServer初始化,初始化过程中传入handler(WSGIRequestHandler

    然后 在BaseWSGIServer 的 serve_forever方法中,HTTPServer调用serve_forever方法。

    因为HTTPServer为BaseServer子类,HTTPServer调用BaseServer的server_forever方法

    BaseServer类 server_forever方法如下
    class BaseServer:
    
        """
    .......
    """ timeout = None
        def __init__(self, server_address, RequestHandlerClass):
            """Constructor.  May be extended, do not override."""
            self.server_address = server_address
            self.RequestHandlerClass = RequestHandlerClass
            self.__is_shut_down = threading.Event()
            self.__shutdown_request = False



    def serve_forever(self, poll_interval=0.5): """Handle one request at a time until shutdown. Polls for shutdown every poll_interval seconds. Ignores self.timeout. If you need to do periodic tasks, do them in another thread. """ self.__is_shut_down.clear() try: # XXX: Consider using another file descriptor or connecting to the # socket to wake this up instead of polling. Polling reduces our # responsiveness to a shutdown request and wastes cpu at all other # times. with _ServerSelector() as selector: selector.register(self, selectors.EVENT_READ) while not self.__shutdown_request: ready = selector.select(poll_interval) # bpo-35017: shutdown() called during select(), exit immediately. if self.__shutdown_request: break if ready: self._handle_request_noblock()#第一步 self.service_actions() finally: self.__shutdown_request = False self.__is_shut_down.set() def _handle_request_noblock(self): """Handle one request, without blocking. I assume that selector.select() has returned that the socket is readable before this function was called, so there should be no risk of blocking in get_request(). """ try: request, client_address = self.get_request() except OSError: return if self.verify_request(request, client_address): try: self.process_request(request, client_address)#第二步 except Exception: self.handle_error(request, client_address) self.shutdown_request(request) except: self.shutdown_request(request) raise else: self.shutdown_request(request) def process_request(self, request, client_address): """Call finish_request. Overridden by ForkingMixIn and ThreadingMixIn. """ self.finish_request(request, client_address)#第三步 self.shutdown_request(request) def finish_request(self, request, client_address): """Finish one request by instantiating RequestHandlerClass.""" self.RequestHandlerClass(request, client_address, self)#第四步WSGIRequestHandler实例化

    server_forever方法中调用 _handle_request_noblock处理请求,由上述代码,最后finish_request 方法处理请求,finish_request 方法中 将WSGIRequestHandler实例化,来处理request
    (self.RequestHandlerClass就是WSGIRequestHandler)

    接下来 解析RequestHandlerClass,由上代码可知RequestHandlerClass即WSGIRequestHandler

    WSGIRequestHandler类   WSGIRequestHandler实例化时调用了其父类BaseRequestHandler的__init__方法

    WSGIRequestHandler是BaseHTTPRequestHandler子类,BaseHTTPRequestHandler是socketserver.StreamRequestHandler子类

    StreamRequestHandler是BaseRequestHandler的子类
    class WSGIRequestHandler(BaseHTTPRequestHandler, object):
    
        """A request handler that implements WSGI dispatching."""
    
        @property
        def server_version(self):
            return 'Werkzeug/' + werkzeug.__version__
    
        def make_environ(self):
            request_url = url_parse(self.path)
    
            def shutdown_server():
                self.server.shutdown_signal = True
    
            url_scheme = self.server.ssl_context is None and 'http' or 'https'
            path_info = url_unquote(request_url.path)
    
            environ = {
                'wsgi.version':         (1, 0),
                'wsgi.url_scheme':      url_scheme,
                'wsgi.input':           self.rfile,
                'wsgi.errors':          sys.stderr,
                'wsgi.multithread':     self.server.multithread,
                'wsgi.multiprocess':    self.server.multiprocess,
                'wsgi.run_once':        False,
                'werkzeug.server.shutdown': shutdown_server,
                'SERVER_SOFTWARE':      self.server_version,
                'REQUEST_METHOD':       self.command,
                'SCRIPT_NAME':          '',
                'PATH_INFO':            wsgi_encoding_dance(path_info),
                'QUERY_STRING':         wsgi_encoding_dance(request_url.query),
                'REMOTE_ADDR':          self.address_string(),
                'REMOTE_PORT':          self.port_integer(),
                'SERVER_NAME':          self.server.server_address[0],
                'SERVER_PORT':          str(self.server.server_address[1]),
                'SERVER_PROTOCOL':      self.request_version
            }
    
            for key, value in self.headers.items():
                key = key.upper().replace('-', '_')
                if key not in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
                    key = 'HTTP_' + key
                environ[key] = value
    
            if environ.get('HTTP_TRANSFER_ENCODING', '').strip().lower() == 'chunked':
                environ['wsgi.input_terminated'] = True
                environ['wsgi.input'] = DechunkedInput(environ['wsgi.input'])
    
            if request_url.scheme and request_url.netloc:
                environ['HTTP_HOST'] = request_url.netloc
    
            return environ
    
        def run_wsgi(self):
            if self.headers.get('Expect', '').lower().strip() == '100-continue':
                self.wfile.write(b'HTTP/1.1 100 Continue
    
    ')
    
            self.environ = environ = self.make_environ()
            headers_set = []
            headers_sent = []
    
            def write(data):
                assert headers_set, 'write() before start_response'
                if not headers_sent:
                    status, response_headers = headers_sent[:] = headers_set
                    try:
                        code, msg = status.split(None, 1)
                    except ValueError:
                        code, msg = status, ""
                    code = int(code)
                    self.send_response(code, msg)
                    header_keys = set()
                    for key, value in response_headers:
                        self.send_header(key, value)
                        key = key.lower()
                        header_keys.add(key)
                    if not ('content-length' in header_keys or
                            environ['REQUEST_METHOD'] == 'HEAD' or
                            code < 200 or code in (204, 304)):
                        self.close_connection = True
                        self.send_header('Connection', 'close')
                    if 'server' not in header_keys:
                        self.send_header('Server', self.version_string())
                    if 'date' not in header_keys:
                        self.send_header('Date', self.date_time_string())
                    self.end_headers()
    
                assert isinstance(data, bytes), 'applications must write bytes'
                self.wfile.write(data)
                self.wfile.flush()
    
            def start_response(status, response_headers, exc_info=None):
                if exc_info:
                    try:
                        if headers_sent:
                            reraise(*exc_info)
                    finally:
                        exc_info = None
                elif headers_set:
                    raise AssertionError('Headers already set')
                headers_set[:] = [status, response_headers]
                return write
    
            def execute(app):
                application_iter = app(environ, start_response)# Flask 的__call__方法被调用
                try:
                    for data in application_iter:
                        write(data)
                    if not headers_sent:
                        write(b'')
                finally:
                    if hasattr(application_iter, 'close'):
                        application_iter.close()
                    application_iter = None
    
            try:
                execute(self.server.app)
            except (socket.error, socket.timeout) as e:
                self.connection_dropped(e, environ)
            except Exception:
                if self.server.passthrough_errors:
                    raise
                from werkzeug.debug.tbtools import get_current_traceback
                traceback = get_current_traceback(ignore_system_exceptions=True)
                try:
                    # if we haven't yet sent the headers but they are set
                    # we roll back to be able to set them again.
                    if not headers_sent:
                        del headers_set[:]
                    execute(InternalServerError())
                except Exception:
                    pass
                self.server.log('error', 'Error on request:
    %s',
                                traceback.plaintext)
    
        def handle(self):
            """Handles a request ignoring dropped connections."""
            rv = None
            try:
                rv = BaseHTTPRequestHandler.handle(self)
            except (socket.error, socket.timeout) as e:
                self.connection_dropped(e)
            except Exception:
                if self.server.ssl_context is None or not is_ssl_error():
                    raise
            if self.server.shutdown_signal:
                self.initiate_shutdown()
            return rv
    
    
        def initiate_shutdown(self):
            """A horrible, horrible way to kill the server for Python 2.6 and
            later.  It's the best we can do.
            """
            # Windows does not provide SIGKILL, go with SIGTERM then.
            sig = getattr(signal, 'SIGKILL', signal.SIGTERM)
            # reloader active
            if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
                os.kill(os.getpid(), sig)
            # python 2.7
            self.server._BaseServer__shutdown_request = True
            # python 2.6
            self.server._BaseServer__serving = False
    
        def connection_dropped(self, error, environ=None):
            """Called if the connection was closed by the client.  By default
            nothing happens.
            """
    
        def handle_one_request(self):
            """Handle a single HTTP request."""
            self.raw_requestline = self.rfile.readline()
            if not self.raw_requestline:
                self.close_connection = 1
            elif self.parse_request():
                return self.run_wsgi()

    /usr/lib/python3.7/http/server.py

    class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
    
        """
    ......
    """ # The Python system version, truncated to its first component. sys_version = "Python/" + sys.version.split()[0] # The server software version. You may want to override this. # The format is multiple whitespace-separated strings, # where each string is of the form name[/version]. server_version = "BaseHTTP/" + __version__ error_message_format = DEFAULT_ERROR_MESSAGE error_content_type = DEFAULT_ERROR_CONTENT_TYPE # The default request version. This only affects responses up until # the point where the request line is parsed, so it mainly decides what # the client gets back when sending a malformed request line. # Most web servers default to HTTP 0.9, i.e. don't send a status line. default_request_version = "HTTP/0.9" def handle_one_request(self): """Handle a single HTTP request. You normally don't need to override this method; see the class __doc__ string for information on how to handle specific HTTP commands such as GET and POST. """ try: self.raw_requestline = self.rfile.readline(65537) if len(self.raw_requestline) > 65536: self.requestline = '' self.request_version = '' self.command = '' self.send_error(HTTPStatus.REQUEST_URI_TOO_LONG) return if not self.raw_requestline: self.close_connection = True return if not self.parse_request(): # An error code has been sent, just exit return mname = 'do_' + self.command if not hasattr(self, mname): self.send_error( HTTPStatus.NOT_IMPLEMENTED, "Unsupported method (%r)" % self.command) return method = getattr(self, mname) method() self.wfile.flush() #actually send the response if not already done. except socket.timeout as e: #a read or a write timed out. Discard this connection self.log_error("Request timed out: %r", e) self.close_connection = True return def handle(self): """Handle multiple requests if necessary.""" self.close_connection = True self.handle_one_request() while not self.close_connection: self.handle_one_request()

    /usr/lib/python3.7/socketserver.py

    class StreamRequestHandler(BaseRequestHandler):
        rbufsize = -1
        wbufsize = 0

        # A timeout to apply to the request socket, if not None.
        timeout = None

        # Disable nagle algorithm for this socket, if True.
        # Use only when wbufsize != 0, to avoid small packets.
        disable_nagle_algorithm = False

        "....."

        def finish(self):
            if not self.wfile.closed:
                try:
                    self.wfile.flush()
                except socket.error:
                    # A final socket error may have occurred here, such as
                    # the local error ECONNABORTED.
                    pass
            self.wfile.close()
            self.rfile.close()
     

    /usr/lib/python3.7/socketserver.py

    class BaseRequestHandler:
    
        """Base class for request handler classes.
    
        This class is instantiated for each request to be handled.  The
        constructor sets the instance variables request, client_address
        and server, and then calls the handle() method.  To implement a
        specific service, all you need to do is to derive a class which
        defines a handle() method.
    
        The handle() method can find the request as self.request, the
        client address as self.client_address, and the server (in case it
        needs access to per-server information) as self.server.  Since a
        separate instance is created for each request, the handle() method
        can define other arbitrary instance variables.
    
        """
    
        def __init__(self, request, client_address, server):
            self.request = request
            self.client_address = client_address
            self.server = server
            self.setup()
            try:
                self.handle()

    finally: self.finish()

     当WSGIRequestHandler初始化时,先调用了BaseRequestHandler的 __init__方法中的handle方法,根据继承性质,此时应该调用WSGIRequestHandler的handle() 方法,来处理。

  • 相关阅读:
    mysql中的内连接,外连接
    MySQL左连接、右连接
    attempted to assign id from null onetoone
    Hibernate session方法
    java.sql.SQLException: No operations allowed after connection closed.Connection was implicitly closed due to underlying exception/error
    hibernate多对一、一对一、一对多、多对多的配置方法
    齐头并进
    w5100的板子做回来了,再次犯错误。
    一个阶段的任务基本完成了
    任务繁重
  • 原文地址:https://www.cnblogs.com/qiu-hua/p/12631523.html
Copyright © 2011-2022 走看看