WSGI簡介

jopen 9年前發布 | 15K 次閱讀 WSGI

WSGI是什么

WSGI的全稱是 Web Server Gateway Interface ,翻譯過來就是 Web服務器網關接口 。具體的來說, WSGI是一個規范,定義了Web服務器如何與Python應用程序進行交互,使得使用Python寫的Web應用程序可以和Web服務器對接起來 。WSGI一開始是在 PEP-0333 中定義的,最新版本是在Python的 PEP-3333 定義的。

對于初學者來說,上面那段就是廢話,說了跟沒說一樣。本文的主要內容就是說清楚,WSGI到底是如何工作的。

為什么需要WSGI這個規范

在Web部署的方案上,有一個方案是目前應用最廣泛的:

  • 首先,部署一個Web服務器專門用來處理HTTP協議層面相關的事情,比如如何在一個物理機上提供多個不同的Web服務(單IP多域名,單IP多端口等)這種事情。
  • 然后,部署一個用各種語言編寫(Java, PHP, Python, Ruby等)的應用程序,這個應用程序會從Web服務器上接收客戶端的請求,處理完成后,再返回響應給Web服務器,最后由Web服務器返回給客戶端。
  • </ul>

    那么,要采用這種方案,Web服務器和應用程序之間就要知道如何進行交互。為了定義Web服務器和應用程序之間的交互過程,就形成了很多不同的規范。這種規范里最早的一個是 CGI (Common Gateway Interface),1993年開發的。后來又出現了很多這樣的規范。比如改進CGI性能的FasgCGI,Java專用的Servlet規范,還有Python專用的WSGI規范等。提出這些規范的目的就是為了定義統一的標準,提升程序的可移植性。在WSGI規范的最開始的PEP-333中一開始就描述了為什么需要WSGI規范。

    WSGI如何工作

    從上文可以知道,WSGI相當于是Web服務器和Python應用程序之間的橋梁。那么這個橋梁是如何工作的呢?首先,我們明確橋梁的作用,WSGI存在的目的有兩個:

    1. 讓Web服務器知道如何調用Python應用程序,并且把用戶的請求告訴應用程序。
    2. 讓Python應用程序知道用戶的具體請求是什么,以及如何返回結果給Web服務器。
    3. </ol>

      WSGI中的角色

      在WSGI中定義了兩個角色,Web服務器端稱為 server 或者 gateway ,應用程序端稱為 application 或者 framework (因為WSGI的應用程序端的規范一般都是由具體的框架來實現的)。我們下面統一使用server和application這兩個術語。

      server端會先收到用戶的請求,然后會根據規范的要求調用application端,如下圖所示:

      WSGI簡介

      調用的結果會被封裝成HTTP響應后再發送給客戶端。

      server如何調用application

      首先,每個application的入口只有一個,也就是所有的客戶端請求都同一個入口進入到應用程序。

      接下來,server端需要知道去哪里找application的入口。這個需要在server端指定一個Python模塊,也就是Python應用中的一個文件,并且這個模塊中需要包含一個名稱為 application 的可調用對象(函數和類都可以),這個 application 對象就是這個應用程序的唯一入口了。WSGI還定義了 application 對象的形式:

      def simple_app(environ, start_response):
            pass

      上面代碼中的environ和start_response就是server端調用 application 對象時傳遞的兩個參數。

      我們來看具體的例子。假設我們的應用程序的入口文件是/var/www/index.py,那么我們就需要在server端配置好這個路徑(如何配置取決于server端的實現),然后在index.py中的代碼如下所示:

      使用標準庫(這個只是demo)

      import wsgiref

      application = wsgiref.simple_server.demo_app</pre>

      使用web.py框架

      import web
      urls = (
          '/.*', 'hello',
      )
      class hello(object):
          def GET(self):
              return "Hello, world."
      application = web.application(urls, globals()).wsgifunc()

      你可以看到,文件中都需要有一個 application 對象,server端會找到這個文件,然后調用這個對象。所以支持WSGI的Python框架最終都會有這么一個application對象,不過框架的使用者不需要關心這個application對象內部是如何工作的,只需要關心路由定義、請求處理等具體的業務邏輯。

      因為application對象是唯一的入口,所以不管客戶端請求的路徑和數據是什么,server都是調用這個application對象,具體的客戶端請求的處理有application對象完成。

      application對象需要做什么

      上面已經提到了,application對象需要是一個可調用對象,而且其定義需要滿足如下形式:

      def simple_app(environ, start_response):
            pass

      當server按照WSGI的規范調用了application之后,application就可以開始處理客戶端的請求了,處理請求之后,application對象需要返回處理結果給server端。處理請求和返回結果這兩個事情,都和server調用application對象時傳遞的兩個參數有關。

      environ參數

      environ參數是一個Python的字典,里面存放了所有和客戶端相關的信息,這樣application對象就能知道客戶端請求的資源是什么,請求中帶了什么數據等。environ字典包含了一些CGI規范要求的數據,以及WSGI規范新增的數據,還可能包含一些操作系統的環境變量以及Web服務器相關的環境變量。我們來看一些environ中常用的成員:

      首先是CGI規范中要求的變量:

      • REQUEST_METHOD : 請求方法,是個字符串,'GET', 'POST'等
      • SCRIPT_NAME : HTTP請求的path中的用于查找到application對象的部分,比如Web服務器可以根據path的一部分來決定請求由哪個virtual host處理
      • PATH_INFO : HTTP請求的path中剩余的部分,也就是application要處理的部分
      • QUERY_STRING : HTTP請求中的查詢字符串,URL中 ? 后面的內容
      • CONTENT_TYPE : HTTP headers中的content-type內容
      • CONTENT_LENGTH : HTTP headers中的content-length內容
      • SERVER_NAMESERVER_PORT : 服務器名和端口,這兩個值和前面的SCRIPT_NAME, PATH_INFO拼起來可以得到完整的URL路徑
      • SERVER_PROTOCOL : HTTP協議版本,HTTP/1.0或者HTTP/1.1
      • HTTP_ : 和HTTP請求中的headers對應。
      • </ul>

        WSGI規范中還要求environ包含下列成員:

        • wsgi.version :表示WSGI版本,一個元組(1, 0),表示版本1.0
        • wsgi.url_scheme :http或者https
        • wsgi.input :一個類文件的輸入流,application可以通過這個獲取HTTP request body
        • wsgi.errors :一個輸出流,當應用程序出錯時,可以將錯誤信息寫入這里
        • wsgi.multithread :當application對象可能被多個線程同時調用時,這個值需要為True
        • wsgi.multiprocess :當application對象可能被多個進程同時調用時,這個值需要為True
        • wsgi.run_once :當server期望application對象在進程的生命周期內只被調用一次時,該值為True
        • </ul>

          上面列出的這些內容已經包括了客戶端請求的所有數據,足夠application對象處理客戶端請求了。

          start_resposne參數

          start_response是一個可調用對象,接收兩個必選參數和一個可選參數:

          • status : 一個字符串,表示HTTP響應狀態字符串
          • response_headers : 一個列表,包含有如下形式的元組:(header_name, header_value),用來表示HTTP響應的headers
          • exc_info (可選): 用于出錯時,server需要返回給瀏覽器的信息
          • </ul>

            當application對象根據environ參數的內容執行完業務邏輯后,就需要返回結果給server端。我們知道HTTP的響應需要包含status,headers和body,所以在application對象將body作為返回值return之前,需要先調用start_response(),將status和headers的內容返回給server,這同時也是告訴server,application對象要開始返回body了。

            application對象的返回值

            application對象的返回值用于為HTTP響應提供body,如果沒有body,那么可以返回None。如果有body的化,那么需要返回一個可迭代的對象。server端通過遍歷這個可迭代對象可以獲得body的全部內容。

            application demo

            PEP-3333中有一個application的實現demo,我把它再簡化之后如下:

            def simple_app(environ, start_response):
                  status = '200 OK'
                  response_headers = [('Content-type', 'text/plain')]
                  start_response(status, response_headers)
                  return ['hello, world']

            可以將這段代碼和前面的說明對照起來理解。

            再談server如何調用application

            前面已經知道server如何定位到application的入口了,也知道了application的入口的形式以及application對象內部需要完成的工作。那么,我們還需要再說一下,environ和start_response()是需要在server端的生成和定義的,其中關于start_response()的部分在規范中也有明確的要求。這部分內容太長了,不適合放在本文中,有興趣的讀者可以去看下PEP-3333,里面有一段server端的demo實現。

            WSGI中間件

            WSGI Middleware(中間件)也是WSGI規范的一部分。上一章我們已經說明了WSGI的兩個角色:server和application。那么middleware是一種運行在server和application中間的應用(一般都是Python應用)。middleware同時具備server和application角色,對于server來說,它是一個application;對于application來說,它是一個server。middleware并不修改server端和application端的規范,只是同時實現了這兩個角色的功能而已。

            我們可以通過下圖來說明middleware是如何工作的:

            WSGI簡介

            上圖中最上面的三個彩色框表示角色,中間的白色框表示操作,操作的發生順序按照1 ~ 5進行了排序,我們直接對著上圖來說明middleware是如何工作的:

            1. Server收到客戶端的HTTP請求后,生成了environ_s,并且已經定義了start_response_s。
            2. Server調用Middleware的application對象,傳遞的參數是environ_s和start_response_s。
            3. Middleware會根據environ執行業務邏輯,生成environ_m,并且已經定義了start_response_m。
            4. Middleware決定調用Application的application對象,傳遞參數是environ_m和start_response_m。Application的application對象處理完成后,會調用start_response_m并且返回結果給Middleware,存放在result_m中。
            5. Middleware處理result_m,然后生成result_s,接著調用start_response_s,并返回結果result_s給Server端。Server端獲取到result_s后就可以發送結果給客戶端了。
            6. </ol>

              從上面的流程可以看出middleware應用的幾個特點:

              1. Server認為middleware是一個application。
              2. Application認為middleware是一個server。
              3. Middleware可以有多層。
              4. </ol>

                WSGI的實現和部署

                要使用WSGI,需要分別實現server角色和application角色。

                Application端的實現一般是由Python的各種框架來實現的,比如Django, web.py等,一般開發者不需要關心WSGI的實現,框架會會提供接口讓開發者獲取HTTP請求的內容以及發送HTTP響應。

                Server端的實現會比較復雜一點,這個主要是因為軟件架構的原因。一般常用的Web服務器,如Apache和nginx,都不會內置WSGI的支持,而是通過擴展來完成。比如Apache服務器,會通過擴展模塊mod_wsgi來支持WSGI。Apache和mod_wsgi之間通過程序內部接口傳遞信息,mod_wsgi會實現WSGI的server端、進程管理以及對application的調用。Nginx上一般是用proxy的方式,用nginx的協議將請求封裝好,發送給應用服務器,比如uWSGI,應用服務器會實現WSGI的服務端、進程管理以及對application的調用。

                原文 http://segmentfault.com/a/1190000003069785

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!