關於wsgi協議的理解

基礎概念

首先要了解 WSGI 規範的概念,WSGI(Web Server Gateway Interface)規範描述了web server(Gunicorn,uWSGI等)如何與web application(flask, django等)交互、web application如何處理請求,定義在 pep 3333。正是有了 WSGI 規範,咱們才能在任意 web server 上跑各類 web 應用。WSGI API 定義看起來很簡單:html

def application(environ, start_response)
複製代碼
  • application 就是 WSGI app,一個可調用對象python

  • 參數:web

    • environ: 一個包含 WSGI 環境信息的字典,由 WSGI 服務器提供,常見的 key 有 PATH_INFO,QUERY_STRING 等
    • start_response: 生成 WSGI 響應的回調函數,接收兩個參數,status 和 headers
  • 函數返回值爲響應體的迭代器 ###簡單舉例 下面舉個簡單的例子,好比一個返回 hello world 的應用:django

def application(environ, start_response):
    status = '200 OK'
    headers = [('Content-Type', 'text/html; charset=utf8')]
    start_response(status, headers)
    return  [b"<h1>Hello, World!</h1>"]
複製代碼

werkzeug相關

werkzeug是Python實現的WSGI規範的使用函數庫。 正如werkzeug官網Werkzeug上所說,werkzeug使用起來很是簡單,可是卻很是強大。關於使用簡單的這個特性,官網給了一段示例代碼。flask

from werkzeug.wrappers import Request, Response
@Request.application
def application(request):
    return Response('Hello World!')
if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('localhost', 4000, application)
複製代碼

###簡單小結 關於上面的代碼我作一下總結: application--可調用對象,wsig模塊中加括號括號執行 application的返回值--Response對象,wsgi中會對該對象加括號執行其__call__方法 一次成功的訪問,由如下幾步完成瀏覽器

  1. 瀏覽器(client)發送一個請求(request
  2. 服務器(server)接收到請求
  3. 服務器處理請求
  4. 返回處理的結果(response
  5. 瀏覽器處理返回的結果,顯示出來。

Detail

具體來講:bash

  1. wigi相關模塊經過創建socket拿到客戶端發送的數據,而後進行解析,而後封裝到environ中
  2. web框架好比flask,他拿到environ,執行其內部各類調用函數,視圖函數,而後返回Response對象
  3. wigi相關模塊拿到相應的Response對象,執行其__call__方法拿到app_iter對象,進行for循環進行socket.sendall(data)方法進行數據發送 ###源碼 如今咱們開始看一下源碼:
def run_simple(hostname, port, application, use_reloader=False,
               use_debugger=False, use_evalex=True,
               extra_files=None, reloader_interval=1,
               reloader_type='auto', threaded=False,
               processes=1, request_handler=None, static_files=None,
               passthrough_errors=False, ssl_context=None):
    def log_startup(sock):
        display_hostname = hostname not in ('', '*') and hostname or 'localhost'
        if ':' in display_hostname:
            display_hostname = '[%s]' % display_hostname
        quit_msg = '(Press CTRL+C to quit)'
        port = sock.getsockname()[1]
        _log('info', ' * Running on %s://%s:%d/ %s',
             ssl_context is None and 'http' or 'https',
             display_hostname, port, quit_msg)


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

    inner()
複製代碼

執行inner方法 而後執行make_server方法拿到其返回值並賦值給srv服務器

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)
複製代碼

以BaseWSGIServer類爲例,將其實例化就是執行其__init__方法 由於類的各類繼承,我就不一一細說了: 總的來講:app

就是建立socket和定義處理request的類RequestHandleClass
其爲:WSGIRequestHandler,本身看一下源碼找一下吧
複製代碼

而後執行srv.server_forver srvBaseWSGIServer的實例,根據類的繼承,去查找各類方法. 記住一點就是查找方法優先從本身的類定義中找,若是沒有就去父類中找.時刻謹記self是誰框架

#BaseWSGIServer中定義
    def serve_forever(self):
        self.shutdown_signal = False
        try:
            HTTPServer.serve_forever(self)
        except KeyboardInterrupt:
            pass
        finally:
            self.server_close()
複製代碼
###BaseServer
 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()
複製代碼
# BaseServer
    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)
複製代碼

執行process_request方法

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)
複製代碼

Next

def finish_request(self, request, client_address):
        """Finish one request by instantiating RequestHandlerClass."""
        self.RequestHandlerClass(request, client_address, self)
複製代碼

執行RequestHandlerClass類的實例化

執行BaseHTTPRequestHandler的handle方法 WSGIRequestHandler.handle_one_request

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()
複製代碼
def run_wsgi(self):
        if self.headers.get('Expect', '').lower().strip() == '100-continue':
            self.wfile.write(b'HTTP/1.1 100 Continue\r\n\r\n')

        self.environ = environ = self.make_environ()
        headers_set = []
        headers_sent = []

        def write(data):
            assert headers_set, 'write() before start_response'
            if not headers_sent:
                status, response_headers = headers_sent[:] = headers_set
                try:
                    code, msg = status.split(None, 1)
                except ValueError:
                    code, msg = status, ""
                code = int(code)
                self.send_response(code, msg)
                header_keys = set()
                for key, value in response_headers:
                    self.send_header(key, value)
                    key = key.lower()
                    header_keys.add(key)
                if not ('content-length' in header_keys or
                        environ['REQUEST_METHOD'] == 'HEAD' or
                        code < 200 or code in (204, 304)):
                    self.close_connection = True
                    self.send_header('Connection', 'close')
                if 'server' not in header_keys:
                    self.send_header('Server', self.version_string())
                if 'date' not in header_keys:
                    self.send_header('Date', self.date_time_string())
                self.end_headers()

            assert isinstance(data, bytes), 'applications must write bytes'
            self.wfile.write(data)
            self.wfile.flush()

        def start_response(status, response_headers, exc_info=None):
            if exc_info:
                try:
                    if headers_sent:
                        reraise(*exc_info)
                finally:
                    exc_info = None
            elif headers_set:
                raise AssertionError('Headers already set')
            headers_set[:] = [status, response_headers]
            return write

        def execute(app): # app_iter對象 包含了須要返回的各項數據
            application_iter = app(environ, start_response)  # Flask實例的call方法返回的的response對象的__call__方法返回的東西
            try:
                for data in application_iter:
                    write(data)
                if not headers_sent:
                    write(b'')
            finally:
                if hasattr(application_iter, 'close'):
                    application_iter.close()
                application_iter = None

        try:
            execute(self.server.app)
        except (socket.error, socket.timeout) as e:
            self.connection_dropped(e, environ)
        except Exception:
            if self.server.passthrough_errors:
                raise
            from werkzeug.debug.tbtools import get_current_traceback
            traceback = get_current_traceback(ignore_system_exceptions=True)
            try:
                # if we haven't yet sent the headers but they are set
                # we roll back to be able to set them again.
                if not headers_sent:
                    del headers_set[:]
                execute(InternalServerError())
            except Exception:
                pass
            self.server.log('error', 'Error on request:\n%s',
                            traceback.plaintext)
複製代碼

經過這個代碼,咱們拿到了app執行後拿到的可迭代對象 application_iter = app(environ, start_response) # Flask實例的call方法返回的的response對象的__call__方法返回的可迭代對象

END

最終for循環這個對象發送了數據

for data in application_iter:
                    write(data)
複製代碼

參考

相關文章
相關標籤/搜索