欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python 搭建Web站點之Web服務(wù)器網(wǎng)關(guān)接口

 更新時間:2016年11月06日 09:28:49   作者:selfboot  
本文是Python 搭建Web站點系列文章的第二篇,接上文,主要給大家來講述Web服務(wù)器網(wǎng)關(guān)接口WSGI的相關(guān)資料,非常詳細(xì),有需要的小伙伴可以參考下

Python 搭建Web站點之Web服務(wù)器與Web框架 中我們弄清楚了Web 服務(wù)器、Web 應(yīng)用程序、Web框架的概念。對于 Python 來說,越來越多的 Web 框架面世,在給我們更多選擇機會的同時,也限制了我們對于 Web Server 的選擇。同樣是有著很多 Web 框架的Java,因為有著 servlet API 的存在,任何Java Web框架寫的應(yīng)用程序都可以運行在任意一個 Web Server 上。

Python 社區(qū)當(dāng)然也需要這樣一套 API,來適配Web服務(wù)器和應(yīng)用程序,這套 API 就是 WSGI(Python Web Server Gateway Interface),在 PEP 3333 里有詳細(xì)的說明。簡單來說,WSGI是連接Web服務(wù)器和Web應(yīng)用程序的橋梁,一方面從Web server 拿到原始 HTTP 數(shù)據(jù),處理成統(tǒng)一格式后交給 Web 應(yīng)用程序,另一方面從應(yīng)用程序/框架這邊進行業(yè)務(wù)邏輯處理,生成響應(yīng)內(nèi)容后交給服務(wù)器。

Web服務(wù)器和框架通過 WSGI 來進行耦合的詳細(xì)過程如下圖所示:

WSGI Server 適配

具體解釋如下:

應(yīng)用程序(網(wǎng)絡(luò)框架)提供一個命名為application的可調(diào)用對象(WSGI協(xié)議并沒有指定如何實現(xiàn)這個對象)。 服務(wù)器每次從HTTP客戶端接收請求之后,調(diào)用可調(diào)用對象application,調(diào)用時傳遞一個名叫environ的字典作為參數(shù),以及一個名為start_response的可調(diào)用對象。 框架/應(yīng)用生成HTTP狀態(tài)碼以及HTTP響應(yīng)報頭,然后將二者傳遞至start_response,等待服務(wù)器保存。此外,框架/應(yīng)用還將返回響應(yīng)的正文。 服務(wù)器將狀態(tài)碼、響應(yīng)報頭和響應(yīng)正文組合成HTTP響應(yīng),并返回給客戶端(這一步并不屬于WSGI協(xié)議)。

下面分別從服務(wù)器端和應(yīng)用程序端來看看 WSGI 是如何做適配的。

服務(wù)器端

我們知道客戶端(通常是瀏覽器)發(fā)出的每個HTTP請求由請求行、消息報頭、請求正文三部分組成,里面包含了本次請求的相關(guān)細(xì)節(jié)內(nèi)容。比如:

Method:指出在由Request-URI標(biāo)識的資源上所執(zhí)行的方法,包括GET,POST 等 User-Agent:允許客戶端將它的操作系統(tǒng)、瀏覽器和其它屬性告訴服務(wù)器;

服務(wù)器從客戶端接收HTTP請求之后,WSGI 接口必須要對這些請求字段進行統(tǒng)一化處理,方便傳給應(yīng)用服務(wù)器接口(其實就是給框架)。Web服務(wù)器具體傳遞哪些數(shù)據(jù)給應(yīng)用程序,早在CGI(Common Gateway Interface,通用網(wǎng)關(guān)接口)里就有詳細(xì)規(guī)定,這些數(shù)據(jù)被叫做 CGI 環(huán)境變量。WSGI 沿用了 CGI 環(huán)境變量的內(nèi)容,要求 Web 服務(wù)器必須創(chuàng)建一個字典用來保存這些環(huán)境變量(一般將其命名為 environ)。除了 CGI 定義的變量,environ 還必須保存一些WSGI定義的變量,此外還可以保存一些客戶端系統(tǒng)的環(huán)境變量,可以參考environ Variables 來看看具體有哪些變量。

接著 WSGI 接口必須將 environ 交給應(yīng)用程序去處理,這里 WSGI 規(guī)定應(yīng)用程序提供一個可調(diào)用對象 application,然后服務(wù)器去調(diào)用 application,獲得返回值為HTTP響應(yīng)正文。服務(wù)器在調(diào)用 application 的時候,需要提供兩個變量,一個是前面提到的變量字典environ,另一個是可調(diào)用對象 start_response,它產(chǎn)生狀態(tài)碼和響應(yīng)頭,這樣我們就得到了一個完整的HTTP響應(yīng)。Web 服務(wù)器將響應(yīng)返回給客戶端,一次完整的HTTP請求-響應(yīng)過程就完成了。

wsgiref 分析

Python 中內(nèi)置了一個實現(xiàn)了WSGI接口的 Web 服務(wù)器,在模塊wsgiref中,它是用純Python編寫的WSGI服務(wù)器的參考實現(xiàn),我們一起來簡單分析一下它的實現(xiàn)。首先假設(shè)我們用下面代碼啟動一個 Web 服務(wù)器:

# Instantiate the server 
httpd = make_server( 
 'localhost', # The host name 
 8051,   # A port number where to wait for the request 
 application  # The application object name, in this case a function 
) 
# Wait for a single request, serve it and quit 
httpd.handle_request() 

然后我們以Web服務(wù)器接收一個請求、生成 environ,然后調(diào)用 application 來處理請求這條主線來分析源碼的調(diào)用過程,簡化如下圖所示:

WSGI Server 調(diào)用流程

這里主要有三個類,WSGIServer,WSGIRequestHandler,ServerHandle。WSGIServer 是Web服務(wù)器類,可以提供server_address(IP:Port)和 WSGIRequestHandler 類來進行初始化獲得一個server對象。該對象監(jiān)聽響應(yīng)的端口,收到HTTP請求后通過 finish_request 創(chuàng)建一個RequestHandler 類的實例,在該實例的初始化過程中會生成一個 Handle 類實例,然后調(diào)用其 run(application) 函數(shù),在該函數(shù)里面再調(diào)用應(yīng)用程序提供的 application對象來生成響應(yīng)。

這三個類的繼承關(guān)系如下圖所示:

WSGI 類繼承關(guān)系圖

其中 TCPServer 使用 socket 來完成 TCP 通信,HTTPServer 則是用來做 HTTP 層面的處理。同樣的,StreamRequestHandler 來處理 stream socket,BaseHTTPRequestHandler 則是用來處理 HTTP 層面的內(nèi)容,這部分和 WSGI 接口關(guān)系不大,更多的是 Web 服務(wù)器的具體實現(xiàn),可以忽略。

微服務(wù)器實例

如果上面的 wsgiref 過于復(fù)雜的話,下面一起來實現(xiàn)一個微小的 Web 服務(wù)器,便于我們理解 Web 服務(wù)器端 WSGI 接口的實現(xiàn)。代碼摘自 自己動手開發(fā)網(wǎng)絡(luò)服務(wù)器(二),放在 gist 上,主要結(jié)構(gòu)如下:

class WSGIServer(object): 
 # 套接字參數(shù) 
 address_family, socket_type = socket.AF_INET, socket.SOCK_STREAM 
 request_queue_size = 1 
 def __init__(self, server_address): 
  # TCP 服務(wù)端初始化:創(chuàng)建套接字,綁定地址,監(jiān)聽端口 
  # 獲取服務(wù)器地址,端口 
 def set_app(self, application): 
  # 獲取框架提供的 application 
  self.application = application 
 def serve_forever(self): 
  # 處理 TCP 連接:獲取請求內(nèi)容,調(diào)用處理函數(shù) 
 def handle_request(self): 
  # 解析 HTTP 請求,獲取 environ,處理請求內(nèi)容,返回HTTP響應(yīng)結(jié)果 
  env = self.get_environ() 
  result = self.application(env, self.start_response) 
  self.finish_response(result) 
 def parse_request(self, text): 
  # 解析 HTTP 請求 
   
 def get_environ(self): 
  # 分析 environ 參數(shù),這里只是示例,實際情況有很多參數(shù)。 
  env['wsgi.url_scheme'] = 'http' 
  ... 
  env['REQUEST_METHOD'] = self.request_method # GET 
  ... 
  return env 
 def start_response(self, status, response_headers, exc_info=None): 
  # 添加響應(yīng)頭,狀態(tài)碼 
  self.headers_set = [status, response_headers + server_headers] 
 def finish_response(self, result): 
  # 返回 HTTP 響應(yīng)信息 
SERVER_ADDRESS = (HOST, PORT) = '', 8888 
# 創(chuàng)建一個服務(wù)器實例 
def make_server(server_address, application): 
 server = WSGIServer(server_address) 
 server.set_app(application) 
 return server

目前支持 WSGI 的成熟Web服務(wù)器有很多,Gunicorn是相當(dāng)不錯的一個。它脫胎于ruby社區(qū)的Unicorn,成功移植到python上,成為一個WSGI HTTP Server。有以下優(yōu)點:

容易配置 可以自動管理多個worker進程 選擇不同的后臺擴展接口(sync, gevent, tornado等) 應(yīng)用程序端(框架)

和服務(wù)器端相比,應(yīng)用程序端(也可以認(rèn)為框架)要做的事情就簡單很多,它只需要提供一個可調(diào)用對象(一般習(xí)慣將其命名為application),這個對象接收服務(wù)器端傳遞的兩個參數(shù) environ 和 start_response。這里的可調(diào)用對象不僅可以是函數(shù),還可以是類(下面第二個示例)或者擁有 __call__ 方法的實例,總之只要可以接受前面說的兩個參數(shù),并且返回值可以被服務(wù)器進行迭代即可。

Application 具體要做的就是根據(jù) environ 里面提供的關(guān)于 HTTP 請求的信息,進行一定的業(yè)務(wù)處理,返回一個可迭代對象,服務(wù)器端通過迭代這個對象,來獲得 HTTP 響應(yīng)的正文。如果沒有響應(yīng)正文,那么可以返回None。

同時,application 還會調(diào)用服務(wù)器提供的 start_response,產(chǎn)生HTTP響應(yīng)的狀態(tài)碼和響應(yīng)頭,原型如下:

def start_response(self, status, headers,exc_info=None):  

Application 需要提供 status:一個字符串,表示HTTP響應(yīng)狀態(tài)字符串,還有 response_headers: 一個列表,包含有如下形式的元組:(header_name, header_value),用來表示HTTP響應(yīng)的headers。同時 exc_info 是可選的,用于出錯時,server需要返回給瀏覽器的信息。

到這里為止,我們就可以實現(xiàn)一個簡單的 application 了,如下所示:

def simple_app(environ, start_response): 
 """Simplest possible application function""" 
 HELLO_WORLD = "Hello world!\n" 
 status = '200 OK' 
 response_headers = [('Content-type', 'text/plain')] 
 start_response(status, response_headers) 
 return [HELLO_WORLD] 

或者用類實現(xiàn)如下。

class AppClass: 
 """Produce the same output, but using a class""" 
 def __init__(self, environ, start_response): 
  self.environ = environ 
  self.start = start_response 
 def __iter__(self): 
  ... 
  HELLO_WORLD = "Hello world!\n" 
  yield HELLO_WORLD 
注意這里 AppClass 類本身就是 application,用 environ 和 start_response 調(diào)用(實例化)它返回一個實例對象,這個實例對象本身是可迭代的,符合 WSGI 對 application 的要求。

如果想使用 AppClass 類的對象作為 application,那么必須給類添加一個 __call__ 方法,接受 environ 和 start_response 為參數(shù),返回可迭代對象,如下所示:

class AppClass: 
 """Produce the same output, but using an object""" 
 def __call__(self, environ, start_response): 

這部分涉及到python的一些高級特性,比如 yield 和 magic method,可以參考我總結(jié)的python語言要點來理解。

Flask 中的 WSGI

flask 是一個輕量級的Python Web框架,符合 WSGI 的規(guī)范要求。它的最初版本只有 600 多行,相對便于理解。下面我們來看下它最初版本中關(guān)于 WSGI 接口的部分。

def wsgi_app(self, environ, start_response): 
 """The actual WSGI application. 
 This is not implemented in `__call__` so that middlewares can be applied: 
  app.wsgi_app = MyMiddleware(app.wsgi_app) 
 """ 
 with self.request_context(environ): 
  rv = self.preprocess_request() 
  if rv is None: 
   rv = self.dispatch_request() 
  response = self.make_response(rv) 
  response = self.process_response(response) 
  return response(environ, start_response) 
def __call__(self, environ, start_response): 
 """Shortcut for :attr:`wsgi_app`""" 
 return self.wsgi_app(environ, start_response) 

這里的 wsgi_app 實現(xiàn)了我們說的 application 功能,rv 是 對請求的封裝,response 是框架用來處理業(yè)務(wù)邏輯的具體函數(shù)。這里對 flask 源碼不做過多解釋,感興趣的可以去github下載,然后check 到最初版本去查看。

中間件

前面 flask 代碼 wsgi_app 函數(shù)的注釋中提到不直接在 __call__ 中實現(xiàn) application 部分,是為了可以使用中間件。 那么為什么要使用中間件,中間件又是什么呢?

回顧前面的 application/server 端接口,對于一個 HTTP 請求,server 端總是會調(diào)用一個 application 來進行處理,并返回 application 處理后的結(jié)果。這足夠應(yīng)付一般的場景了,不過并不完善,考慮下面的幾種應(yīng)用場景:

對于不同的請求(比如不同的 URL),server 需要調(diào)用不同的 application,那么如何選擇調(diào)用哪個呢; 為了做負(fù)載均衡或者是遠(yuǎn)程處理,需要使用網(wǎng)絡(luò)上其他主機上運行的 application 來做處理; 需要對 application 返回的內(nèi)容做一定處理后才能作為 HTTP 響應(yīng);

上面這些場景有一個共同點就是,有一些必需的操作不管放在服務(wù)端還是應(yīng)用(框架)端都不合適。對應(yīng)用端來說,這些操作應(yīng)該由服務(wù)器端來做,對服務(wù)器端來說,這些操作應(yīng)該由應(yīng)用端來做。為了處理這種情況,引入了中間件。

中間件就像是應(yīng)用端和服務(wù)端的橋梁,來溝通兩邊。對服務(wù)器端來說,中間件表現(xiàn)的像是應(yīng)用端,對應(yīng)用端來說,它表現(xiàn)的像是服務(wù)器端。如下圖所示:

中間件

中間件的實現(xiàn)

flask 框架在 Flask 類的初始化代碼中就使用了中間件:

self.wsgi_app = SharedDataMiddleware(self.wsgi_app, { self.static_path: target }) 

這里的作用和 python 中的裝飾器一樣,就是在執(zhí)行 self.wsgi_app 前后執(zhí)行 SharedDataMiddleware 中的一些內(nèi)容。中間件做的事,很類似python中裝飾器做的事情。SharedDataMiddleware 中間件是 werkzeug 庫提供的,用來支持站點托管靜態(tài)內(nèi)容。此外,還有DispatcherMiddleware 中間件,用來支持根據(jù)不同的請求,調(diào)用不同的 application,這樣就可以解決前面場景 1, 2 中的問題了。

下面來看看 DispatcherMiddleware 的實現(xiàn):

class DispatcherMiddleware(object): 
 """Allows one to mount middlewares or applications in a WSGI application. 
 This is useful if you want to combine multiple WSGI applications:: 
  app = DispatcherMiddleware(app, { 
   '/app2':  app2, 
   '/app3':  app3 
  }) 
 """ 
 def __init__(self, app, mounts=None): 
  self.app = app 
  self.mounts = mounts or {} 
 def __call__(self, environ, start_response): 
  script = environ.get('PATH_INFO', '') 
  path_info = '' 
  while '/' in script: 
   if script in self.mounts: 
    app = self.mounts[script] 
    break 
   script, last_item = script.rsplit('/', 1) 
   path_info = '/%s%s' % (last_item, path_info) 
  else: 
   app = self.mounts.get(script, self.app) 
  original_script_name = environ.get('SCRIPT_NAME', '') 
  environ['SCRIPT_NAME'] = original_script_name + script 
  environ['PATH_INFO'] = path_info 
  return app(environ, start_response) 

初始化中間件時需要提供一個 mounts 字典,用來指定不同 URL 路徑到 application 的映射關(guān)系。這樣對于一個請求,中間件檢查其路徑,然后選擇合適的 application 進行處理。

關(guān)于 WSGI 的原理部分基本結(jié)束,下一篇我會介紹下對 flask 框架的理解。

相關(guān)文章

  • python3爬蟲獲取html內(nèi)容及各屬性值的方法

    python3爬蟲獲取html內(nèi)容及各屬性值的方法

    今天小編就為大家分享一篇python3爬蟲獲取html內(nèi)容及各屬性值的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-12-12
  • Python中工廠模式的實現(xiàn)小結(jié)

    Python中工廠模式的實現(xiàn)小結(jié)

    工廠模式是一種創(chuàng)建型設(shè)計模式,通過定義一個工廠類,將對象的實例化過程封裝起來,本文主要介紹了Python中工廠模式的實現(xiàn)小結(jié),具有一定的參考價值,感興趣的可以了解一下
    2023-11-11
  • python批量處理文件或文件夾

    python批量處理文件或文件夾

    這篇文章主要為大家詳細(xì)介紹了python批量處理文件或文件夾,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • 從Pytorch模型pth文件中讀取參數(shù)成numpy矩陣的操作

    從Pytorch模型pth文件中讀取參數(shù)成numpy矩陣的操作

    這篇文章主要介紹了從Pytorch模型pth文件中讀取參數(shù)成numpy矩陣的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • 解決python3中自定義wsgi函數(shù),make_server函數(shù)報錯的問題

    解決python3中自定義wsgi函數(shù),make_server函數(shù)報錯的問題

    下面小編就為大家分享一篇解決python3中自定義wsgi函數(shù),make_server函數(shù)報錯的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-11-11
  • Python set集合類型操作總結(jié)

    Python set集合類型操作總結(jié)

    這篇文章主要介紹了Python set集合類型操作總結(jié),本文介紹了一個小技巧、去重技巧、創(chuàng)建set、set基本操作等內(nèi)容,需要的朋友可以參考下
    2014-11-11
  • 熵值法原理及Python實現(xiàn)的示例詳解

    熵值法原理及Python實現(xiàn)的示例詳解

    熵值法也稱熵權(quán)法,是學(xué)術(shù)研究及實際應(yīng)用中的一種常用且有效的編制指標(biāo)的方法。本文就來和大家聊聊熵值法原理及Python實現(xiàn),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-02-02
  • pandas 缺失值與空值處理的實現(xiàn)方法

    pandas 缺失值與空值處理的實現(xiàn)方法

    這篇文章主要介紹了pandas 缺失值與空值處理的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • Pandas之DataFrame對象的列和索引之間的轉(zhuǎn)化

    Pandas之DataFrame對象的列和索引之間的轉(zhuǎn)化

    這篇文章主要介紹了Pandas之DataFrame對象的列和索引之間的轉(zhuǎn)化,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • 利用Python半自動化生成Nessus報告的方法

    利用Python半自動化生成Nessus報告的方法

    這篇文章主要介紹了利用Python半自動化生成Nessus報告的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-03-03

最新評論