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

flask 程序運行起來后就在本地建立起了wsgi server ,監(jiān)聽了本地的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對象。并執(zhí)行了該對象的serve_forever()來建立起本地wsgi server的肌毅。
繼續(xù)分析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 會根據(jù)app.run()時提供的參數(shù)決定采用什么方式工作默認是線程模式筷转。
app.run()中提供的參數(shù)是processes=n 時,1<n<40,用多進程方式工作芽腾。多進程方式時旦装,make_server會返回一個ForkingWSGIServer對象。
app.run()中提供的參數(shù)是threaded=True時摊滔,用多線程方式工作阴绢。這時make_server會返回一個ThreadedWSGIServer對象。
不管用哪種方式工作艰躺,都會實現(xiàn)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。

繼續(xù)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在后面會被觸發(fā)左电。
繼續(xù)父類HTTPServer的分析HTTPServer(socketserver.TCPServer)。
HTTPServer其實啥也沒做页响,只是繼承了socketserver.TCPServer篓足。繼續(xù)對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類中實現(xiàn)了serve.forever方法栈拖,并最終對self.RequestHandlerClass 即WSGIRequestHandler進行了實例化。BaseServer類的process_request方法在ForkingMixIn和 ThreadingMixIn中進程了重寫没陡。在多進程和多線程模式下會調(diào)用各自的process_request涩哟。

ForkingMixIn和ThreadingMixIn分別是ForkingWSGIServer和ThreadedWSGIServer的父類,F(xiàn)orkingWSGIServer和ThreadedWSGIServer是實現(xiàn)多進程和多線程的重要類盼玄。
現(xiàn)在再回頭去看看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()

多線程方式會調(diào)用ThreadingMixIn.process_request,在該方法中創(chuàng)建線程,self.process_request_thread又調(diào)用了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。

現(xiàn)在分析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 的實例化最終是執(zhí)行了BaseRequestHandler的__init__并觸發(fā)了handle()方法的執(zhí)行暴心。
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執(zhí)行流程為 handler()-> BaseHTTPRequestHandler.handler() -> WSGIRequestHandler.handle_one_request() .在handle_one_request()中最終執(zhí)行了run_wsgi().run_wsgi()中的def execute(app)會最終觸發(fā)self.app()的執(zhí)行execute(self.server.app)杂拨。
self.app 就是Flask對象。這個app()的執(zhí)行悯衬,也即為Flask 類里面的__call__方法的執(zhí)行弹沽。隨后將進入上下文處理階段。
Flask源碼剖析二

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末筋粗,一起剝皮案震驚了整個濱河市策橘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌娜亿,老刑警劉巖丽已,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異买决,居然都是意外死亡沛婴,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門督赤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嘁灯,“玉大人,你說我怎么就攤上這事躲舌〕笮觯” “怎么了?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵没卸,是天一觀的道長羹奉。 經(jīng)常有香客問我,道長约计,這世上最難降的妖魔是什么诀拭? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮病蛉,結果婚禮上炫加,老公的妹妹穿的比我還像新娘。我一直安慰自己铺然,他們只是感情好俗孝,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著魄健,像睡著了一般赋铝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沽瘦,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天革骨,我揣著相機與錄音农尖,去河邊找鬼。 笑死良哲,一個胖子當著我的面吹牛盛卡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播筑凫,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼滑沧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了巍实?” 一聲冷哼從身側響起滓技,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎棚潦,沒想到半個月后令漂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡丸边,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年叠必,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片原环。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡挠唆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嘱吗,到底是詐尸還是另有隱情玄组,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布谒麦,位于F島的核電站俄讹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏绕德。R本人自食惡果不足惜患膛,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望耻蛇。 院中可真熱鬧踪蹬,春花似錦、人聲如沸臣咖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽夺蛇。三九已至疚漆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背娶聘。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工闻镶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人丸升。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓铆农,卻偏偏與公主長得像,于是被迫代替她去往敵國和親发钝。 傳聞我的和親對象是個殘疾皇子顿涣,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容