当前位置: 移动技术网 > IT编程>脚本编程>Python > flask源码解读01: app.run()的背后

flask源码解读01: app.run()的背后

2018年04月27日  | 移动技术网IT编程  | 我要评论

虹桥机场机长获奖,云霄方芳,电玩菠菜

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

Flask定义的run方法如下:

    def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
        # Change this into a no-op if the server is invoked from the
        # command line.  Have a look at cli.py for more information.
        if os.environ.get('FLASK_RUN_FROM_CLI') == 'true':
            from .debughelpers import explain_ignored_app_run
            explain_ignored_app_run()
            return

        if load_dotenv:
            from flask.cli import load_dotenv
            load_dotenv()

        if debug is not None:
            self._reconfigure_for_run_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
        port = int(port or sn_port or _port)
        options.setdefault('use_reloader', self.debug)
        options.setdefault('use_debugger', self.debug)

        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

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

def run_simple(hostname, port, application, use_reloader=False,
               extra_files=None, threaded=False, processes=1):
    """
    Start an application using wsgiref and with an optional reloader.
    """
    def inner():
        srv = make_server(hostname, port, application, threaded,
                          processes)
        try:
            srv.serve_forever()
        except KeyboardInterrupt:
            pass

    if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
        print '* Running on http://%s:%d/' % (hostname or '0.0.0.0', port)
    if use_reloader:
        # Create and destroy a socket so that any exceptions are raised before we
        # spawn a separate Python interpreter and loose this ability.
        test_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        test_socket.bind((hostname, port))
        test_socket.close()
        run_with_reloader(inner, extra_files or [])
    else:
        inner()

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

 

server_forever函数如下:

def make_server(host, port, app=None, threaded=False, processes=1):
    """
    Create a new wsgiref server 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:
        class handler(BaseRequestHandler):
            multithreaded = True
        class server(ThreadingMixIn, WSGIServer):
            pass
    elif processes > 1:
        class handler(BaseRequestHandler):
            multiprocess = True
            max_children = processes - 1
        class server(ForkingMixIn, WSGIServer):
            pass
    else:
        handler = BaseRequestHandler
        server = WSGIServer
    srv = server((host, port), handler)
    srv.set_app(app)
    return srv

make_server函数中, 首先根据threaded和multiprocess参数确定要采用的handler和server类. 这两个参数的作用是指定server运行在多线程或者多进程模式下. 默认情况下不采用多线程多进程.

 

先看上面默认采用的server类: WSGIServer

class WSGIServer(HTTPServer):

    """BaseHTTPServer that implements the Python WSGI protocol"""

    application = None

    def server_bind(self):
        """Override server_bind to store the server name."""
        HTTPServer.server_bind(self)
        self.setup_environ()

    def setup_environ(self):
        # Set up base environment
        env = self.base_environ = {}
        env['SERVER_NAME'] = self.server_name
        env['GATEWAY_INTERFACE'] = 'CGI/1.1'
        env['SERVER_PORT'] = str(self.server_port)
        env['REMOTE_HOST']=''
        env['CONTENT_LENGTH']=''
        env['SCRIPT_NAME'] = ''

    def get_app(self):
        return self.application

    def set_app(self,application):
        self.application = application

WSGIServer继承自HTTPServer, HTTPServer来自BaseHTTPServer模块

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.socket.getsockname()[:2]
        self.server_name = socket.getfqdn(host)
        self.server_port = port

HTTPServer继承自SocketServer模块的TCPServer

class TCPServer(BaseServer):

    address_family = socket.AF_INET

    socket_type = socket.SOCK_STREAM

    request_queue_size = 5

    allow_reuse_address = False

    def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
        """Constructor.  May be extended, do not override."""
        BaseServer.__init__(self, server_address, RequestHandlerClass)
        self.socket = socket.socket(self.address_family,
                                    self.socket_type)
        if bind_and_activate:
            try:
                self.server_bind()    # socket.bind
                self.server_activate()    # socket.listen 
            except:
                self.server_close()
                raise

    def server_bind(self):
        """Called by constructor to bind the socket.

        May be overridden.

        """
        if self.allow_reuse_address:
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(self.server_address)
        self.server_address = self.socket.getsockname()

    def server_activate(self):
        """Called by constructor to activate the server.

        May be overridden.

        """
        self.socket.listen(self.request_queue_size)

    def server_close(self):
        """Called to clean-up the server.

        May be overridden.

        """
        self.socket.close()

    def fileno(self):
        """Return socket file number.

        Interface required by selector.

        """
        return self.socket.fileno()

    def get_request(self):
        """Get the request and client address from the socket.

        May be overridden.

        """
        return self.socket.accept()    # 在轮寻之后调用socket.accept

    def shutdown_request(self, request):
        """Called to shutdown and close an individual request."""
        try:
            #explicitly shutdown.  socket.close() merely releases
            #the socket and waits for GC to perform the actual close.
            request.shutdown(socket.SHUT_WR)
        except OSError:
            pass #some platforms may raise ENOTCONN here
        self.close_request(request)

    def close_request(self, request):
        """Called to clean up an individual request."""
        request.close()

TCPServer继承自BaseServer

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 server_activate(self): """Called by constructor to activate the server. May be overridden. """ pass 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) if ready: self._handle_request_noblock() self.service_actions() finally: self.__shutdown_request = False self.__is_shut_down.set() def shutdown(self): """Stops the serve_forever loop. Blocks until the loop has finished. This must be called while serve_forever() is running in another thread, or it will deadlock. """ self.__shutdown_request = True self.__is_shut_down.wait() def service_actions(self): """Called by the serve_forever() loop. May be overridden by a subclass / Mixin to implement any code that needs to be run during the loop. """ pass # The distinction between handling, getting, processing and finishing a # request is fairly arbitrary. Remember: # # - handle_request() is the top-level call. It calls selector.select(), # get_request(), verify_request() and process_request() # - get_request() is different for stream or datagram sockets # - process_request() is the place that may fork a new process or create a # new thread to finish the request # - finish_request() instantiates the request handler class; this # constructor will handle the request all by itself def handle_request(self): """Handle one request, possibly blocking. Respects self.timeout. """ # Support people who used socket.settimeout() to escape # handle_request before self.timeout was available. timeout = self.socket.gettimeout() if timeout is None: timeout = self.timeout elif self.timeout is not None: timeout = min(timeout, self.timeout) if timeout is not None: deadline = time() + timeout # Wait until a request arrives or the timeout expires - the loop is # necessary to accommodate early wakeups due to EINTR. with _ServerSelector() as selector: selector.register(self, selectors.EVENT_READ) while True: ready = selector.select(timeout) if ready: return self._handle_request_noblock() else: if timeout is not None: timeout = deadline - time() if timeout < 0: return self.handle_timeout() 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 handle_timeout(self): """Called if no new request arrives within self.timeout. Overridden by ForkingMixIn. """ pass def verify_request(self, request, client_address): """Verify the request. May be overridden. Return True if we should proceed with this request. """ return True 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 server_close(self): """Called to clean-up the server. May be overridden. """ pass def finish_request(self, request, client_address): """Finish one request by instantiating RequestHandlerClass.""" self.RequestHandlerClass(request, client_address, self) def shutdown_request(self, request): """Called to shutdown and close an individual request.""" self.close_request(request) def close_request(self, request): """Called to clean up an individual request.""" pass def handle_error(self, request, client_address): """Handle an error gracefully. May be overridden. The default is to print a traceback and continue. """ print('-'*40, file=sys.stderr) # 打印40个'-' print('Exception happened during processing of request from', client_address, file=sys.stderr) import traceback traceback.print_exc() print('-'*40, file=sys.stderr) def __enter__(self): return self def __exit__(self, *args): self.server_close()

在TCPServer.__init__方法中, 首先调用BaseServer.__init__对实例初始化, BaseServer.__init__的作用主要是为实例绑定地址和RequestHandlerClass. 接下来先指定socket属性: self.socket = socket.socket(self.address_family, self.socket_type), 再t调用self.server_bind和self.server_activate. 这是TCP 监听socket的处理流程. 之后self.socket便在指定的端口监听是否有连接到来. 

通过文章开始的run_simple函数知道, 在创建Server实例srv之后, 会调用serve_forever方法. serve_forever方法在BaseServer中定义. BaseServer方法主要运用多路复用技术监视socket, 如果有socket可读, 则调用_handle_request_noblock方法. _handle_request_noblock主要调用process_request, process_request调用finish_request, 在finish_request中调用self.RequestHandlerClass(request, client_address, self)新建Handler实例处理请求

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网