当我们用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实例处理请求