1. 程式人生 > >flask 原始碼淺析(flask 如何處理請求(多執行緒,多程序,IO多路複用))

flask 原始碼淺析(flask 如何處理請求(多執行緒,多程序,IO多路複用))

之前有閱讀過tornado 底層的實現,tornado 為了解決C10K 問題(沒聽說過C10K問題的請檢視: http://www.360doc.com/content/13/0522/18/1542811_287328391.shtml),在Linux 平臺下是使用了epoll(python2.6 開始支援epoll),unix 平臺下 tornado 使用了kque , 由於flask 之前沒有看過底層的實現,因此趁著清明假期看了一下flask,到底是來一個請求使用一個執行緒呢,還是程序呢,還是IO多路複用。

涉及到的原始碼檔案

site-packages/flask/app.py, site-packages/werkzeug/serving.py, Lib/socketserver.py

首先肯定要先看入口函式啦

app.py 裡面的 run 函式

def run(self, host=None, port=None, debug=None, **options):

該函式通過 run_simple(host, port, self, **options) 啟動了socket 伺服器(無論是哪個web框架,其實底層都是使用socketserver 監聽在某個套接字上來處理請求的)

run_simple 然後到呼叫 serving.py 裡面的make_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)
:
"""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)

可以看到flask 為我們提供了三種方式來處理請求
1 使用多執行緒來進行處理
2 使用多程序來進行處理
3 使用poll 或者 select IO多路複用的方式進行處理

BaseWSGIServer 這個類是使用IO 多路複用的
下面有個方法 start_forever

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

我們主要來看HttpServer.serve_forever方法

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

_ServerSelector() 定義了 到底該使用select 還是 poll

# poll/select have the advantage of not requiring any extra file descriptor,
# contrarily to epoll/kqueue (also, they require a single syscall).
if hasattr(selectors, 'PollSelector'):
    _ServerSelector = selectors.PollSelector
else:
    _ServerSelector = selectors.SelectSelector

對於IO多路複用不熟悉的,推薦檢視該文章: https://blog.csdn.net/qq546770908/article/details/53082870