Flask源码剖析一之建立本地WSGIServer

flask 程序运行起来后就在本地建立起了wsgi server ,监听了本地的5000端口,本文从app.run()开始追踪wsgiserver 建立的过程。
flask.app.Flask.run
这个方法中最重要的一句是run_simple(host, port, self, **options),注意该方法传入self,其实就是app方法。
werkzeug.serving.run_simple

    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()

在run_simple方法中有个inner方法,该方法实例化了make_server对象。并执行了该对象的serve_forever()来建立起本地wsgi server的。
继续分析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:
        #IO多路复用
        return BaseWSGIServer(
            host, port, app, request_handler, passthrough_errors, ssl_context, fd=fd
        )

make_server 会根据app.run()时提供的参数决定采用什么方式工作默认是线程模式。
app.run()中提供的参数是processes=n 时,1<n<40,用多进程方式工作。多进程方式时,make_server会返回一个ForkingWSGIServer对象。
app.run()中提供的参数是threaded=True时,用多线程方式工作。这时make_server会返回一个ThreadedWSGIServer对象。
不管用哪种方式工作,都会实现BaseWSGIServer 对象。

class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer):

    """A WSGI server that does forking."""

    multiprocess = True

    def __init__(
        self,
        host,
        port,
        app,
        processes=40,
        handler=None,
        passthrough_errors=False,
        ssl_context=None,
        fd=None,
    ):
        if not can_fork:
            raise ValueError("Your platform does not support forking.")
        BaseWSGIServer.__init__(
            self, host, port, app, handler, passthrough_errors, ssl_context, fd
        )
        self.max_children = processes

class ThreadedWSGIServer(ThreadingMixIn, BaseWSGIServer):

    """A WSGI server that does threading."""

    multithread = True
    daemon_threads = True

从上面的代码中可以看出不管是多进程的ForkingWSGIServer还是多线程的 ThreadedWSGIServer 都继承了BaseWSGIServer。

继续BaseWSGIServer的分析
werkzeug.serving.BaseWSGIServer

class BaseWSGIServer(HTTPServer, object):
  def __init__(
        self,
        host,
        port,
        app,
        handler=None,
        passthrough_errors=False,
        ssl_context=None,
        fd=None,
    ):
        if handler is None:
            handler = WSGIRequestHandler

        HTTPServer.__init__(self, server_address, handler)

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

这段代码中将WSGIRequestHandler注册到了BaseWSGIServer,WSGIRequestHandler在后面会被触发。
继续父类HTTPServer的分析HTTPServer(socketserver.TCPServer)。
HTTPServer其实啥也没做,只是继承了socketserver.TCPServer。继续对TCPServer进行分析。
socketserver

class TCPServer(BaseServer):
    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()
                self.server_activate()
            except:
                self.server_close()
                raise

TCPServer类扩展了BaseServer并将WSGIRequestHandler传给了BaseServer。
socketserver

class BaseServer:
    def serve_forever(self, poll_interval=0.5):
         self._handle_request_noblock()

   def _handle_request_noblock(self):
         self.process_request(request, client_address)

    def process_request(self, request, client_address):
        """Call finish_request.
        Overridden by ForkingMixIn and ThreadingMixIn.
        """
        self.finish_request(request, client_address)

    def finish_request(self, request, client_address):
        self.RequestHandlerClass(request, client_address, self)

BaseServer类中实现了serve.forever方法,并最终对self.RequestHandlerClass 即WSGIRequestHandler进行了实例化。BaseServer类的process_request方法在ForkingMixIn和 ThreadingMixIn中进程了重写。在多进程和多线程模式下会调用各自的process_request。

ForkingMixIn和ThreadingMixIn分别是ForkingWSGIServer和ThreadedWSGIServer的父类,ForkingWSGIServer和ThreadedWSGIServer是实现多进程和多线程的重要类。
现在再回头去看看ForkingMixIn和 ThreadingMixIn中的process_request 。
socketserver.ForkingMixIn
多进程处理类

 class ForkingMixIn:
       def process_request(self, request, client_address):
            """Fork a new subprocess to process the request."""
            pid = os.fork()
            if pid:
                # Parent process
                if self.active_children is None:
                    self.active_children = set()
                self.active_children.add(pid)
                self.close_request(request)
                return
            else:
                # Child process.
                # This must never return, hence os._exit()!
                status = 1
                try:
                    self.finish_request(request, client_address)
                    status = 0
                except Exception:
                    self.handle_error(request, client_address)
                finally:
                    try:
                        self.shutdown_request(request)
                    finally:
                        os._exit(status)

多线程处理类

class ThreadingMixIn:
     def process_request_thread(self, request, client_address):
        """Same as in BaseServer but as a thread.

        In addition, exception handling is done here.

        """
        try:
            self.finish_request(request, client_address)
        except Exception:
            self.handle_error(request, client_address)
        finally:
            self.shutdown_request(request)

    def process_request(self, request, client_address):
        """Start a new thread to process the request."""
        t = threading.Thread(target = self.process_request_thread,
                             args = (request, client_address))
        t.daemon = self.daemon_threads
        if not t.daemon and self.block_on_close:
            if self._threads is None:
                self._threads = []
            self._threads.append(t)
        t.start()

多线程方式会调用ThreadingMixIn.process_request,在该方法中创建线程,self.process_request_thread又调用了self.finish_request(request, client_address),回到BaseServer中的finish_request

 def finish_request(self, request, client_address):
        self.RequestHandlerClass(request, client_address, self)

self.RequestHandlerClass(),也就是实例化WSGIRequestHandler。

现在分析WSGIRequestHandler,WSGIRequestHandler是一个多继承类

werkzeug.serving
class WSGIRequestHandler(BaseHTTPRequestHandler, object)
http.server
class BaseHTTPRequestHandler(socketserver.StreamRequestHandler)
socketserver
class StreamRequestHandler(BaseRequestHandler)
socketserver

class BaseRequestHandler:
      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()

    def setup(self):
        pass

    def handle(self):
        pass

    def finish(self):
        pass

由此可见,对WSGIRequestHandler 的实例化最终是执行了BaseRequestHandler的__init__并触发了handle()方法的执行。
WSGIRequestHandler对handle方法进行了重写。

class WSGIRequestHandler(BaseHTTPRequestHandler, object):
   def run_wsgi(self):
        def execute(app):
            application_iter = app(environ, start_response)   
            try:
                 execute(self.server.app)
       
        def handle(self):
        """Handles a request ignoring dropped connections."""
        rv = None
        try:
            rv = BaseHTTPRequestHandler.handle(self)
  
       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()


class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):  
   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()

WSGIRequestHandler的handle执行流程为 handler()-> BaseHTTPRequestHandler.handler() -> WSGIRequestHandler.handle_one_request() .在handle_one_request()中最终执行了run_wsgi().run_wsgi()中的def execute(app)会最终触发self.app()的执行execute(self.server.app)。
self.app 就是Flask对象。这个app()的执行,也即为Flask 类里面的__call__方法的执行。随后将进入上下文处理阶段。
Flask源码剖析二

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容