2018-06-09容錯(cuò)+網(wǎng)絡(luò)資料(WSGI詳解)

在web服務(wù)器運(yùn)行中,為了防止請(qǐng)求的文件打開有異常豪墅,要在判斷文件是否以“.py”結(jié)尾之后加上異常調(diào)試泉手。

            try:
                file = open(HTML_ROOT_DIR + file_name, "rb")
            except IOError:
                response_start_line = "HTTP/1.1 404 NOT FOUND\r\n"
                response_headers = "Sever: My Sever\r\n"
                response_body = "The file is not found!"
            else:
                file_data = file.read()
                file.close()

                # 構(gòu)造響應(yīng)數(shù)據(jù)
                response_start_line = "HTTP/1.1 200 OK\r\n"
                response_headers = "Sever: My Sever\r\n"
                response_body = file_data.decode("utf-8")

            response = response_start_line + response_headers + "\r\n" + response_body
            print("response data:", response)

搜索到了一篇博文,講解WSGI的使用(來源于:https://segmentfault.com/a/1190000003069785):

WSGI是什么

WSGI的全稱是Web Server Gateway Interface偶器,翻譯過來就是Web服務(wù)器網(wǎng)關(guān)接口斩萌。具體的來說,WSGI是一個(gè)規(guī)范屏轰,定義了Web服務(wù)器如何與Python應(yīng)用程序進(jìn)行交互颊郎,使得使用Python寫的Web應(yīng)用程序可以和Web服務(wù)器對(duì)接起來。WSGI一開始是在PEP-0333中定義的霎苗,最新版本是在Python的PEP-3333定義的姆吭。

對(duì)于初學(xué)者來說,上面那段就是廢話唁盏,說了跟沒說一樣内狸。本文的主要內(nèi)容就是說清楚检眯,WSGI到底是如何工作的。

為什么需要WSGI這個(gè)規(guī)范

在Web部署的方案上昆淡,有一個(gè)方案是目前應(yīng)用最廣泛的:

  • 首先锰瘸,部署一個(gè)Web服務(wù)器專門用來處理HTTP協(xié)議層面相關(guān)的事情,比如如何在一個(gè)物理機(jī)上提供多個(gè)不同的Web服務(wù)(單IP多域名瘪撇,單IP多端口等)這種事情。

  • 然后港庄,部署一個(gè)用各種語(yǔ)言編寫(Java, PHP, Python, Ruby等)的應(yīng)用程序倔既,這個(gè)應(yīng)用程序會(huì)從Web服務(wù)器上接收客戶端的請(qǐng)求,處理完成后鹏氧,再返回響應(yīng)給Web服務(wù)器渤涌,最后由Web服務(wù)器返回給客戶端。

那么把还,要采用這種方案实蓬,Web服務(wù)器和應(yīng)用程序之間就要知道如何進(jìn)行交互。為了定義Web服務(wù)器和應(yīng)用程序之間的交互過程吊履,就形成了很多不同的規(guī)范安皱。這種規(guī)范里最早的一個(gè)是CGI][3,1993年開發(fā)的艇炎。后來又出現(xiàn)了很多這樣的規(guī)范酌伊。比如改進(jìn)CGI性能的FasgCGI,Java專用的Servlet規(guī)范缀踪,還有Python專用的WSGI規(guī)范等居砖。提出這些規(guī)范的目的就是為了定義統(tǒng)一的標(biāo)準(zhǔn),提升程序的可移植性驴娃。在WSGI規(guī)范的最開始的PEP-333中一開始就描述了為什么需要WSGI規(guī)范奏候。

WSGI如何工作

從上文可以知道,WSGI相當(dāng)于是Web服務(wù)器和Python應(yīng)用程序之間的橋梁唇敞。那么這個(gè)橋梁是如何工作的呢蔗草?首先,我們明確橋梁的作用疆柔,WSGI存在的目的有兩個(gè):

  1. 讓W(xué)eb服務(wù)器知道如何調(diào)用Python應(yīng)用程序蕉世,并且把用戶的請(qǐng)求告訴應(yīng)用程序。

  2. 讓Python應(yīng)用程序知道用戶的具體請(qǐng)求是什么婆硬,以及如何返回結(jié)果給Web服務(wù)器狠轻。

WSGI中的角色

在WSGI中定義了兩個(gè)角色,Web服務(wù)器端稱為server或者gateway彬犯,應(yīng)用程序端稱為application或者framework(因?yàn)閃SGI的應(yīng)用程序端的規(guī)范一般都是由具體的框架來實(shí)現(xiàn)的)向楼。我們下面統(tǒng)一使用server和application這兩個(gè)術(shù)語(yǔ)查吊。

server端會(huì)先收到用戶的請(qǐng)求,然后會(huì)根據(jù)規(guī)范的要求調(diào)用application端湖蜕,如下圖所示:

[圖片上傳失敗...(image-527be2-1528548743542)]

調(diào)用的結(jié)果會(huì)被封裝成HTTP響應(yīng)后再發(fā)送給客戶端逻卖。

server如何調(diào)用application

首先,每個(gè)application的入口只有一個(gè)昭抒,也就是所有的客戶端請(qǐng)求都同一個(gè)入口進(jìn)入到應(yīng)用程序评也。

接下來,server端需要知道去哪里找application的入口灭返。這個(gè)需要在server端指定一個(gè)Python模塊盗迟,也就是Python應(yīng)用中的一個(gè)文件,并且這個(gè)模塊中需要包含一個(gè)名稱為application的可調(diào)用對(duì)象(函數(shù)和類都可以)熙含,這個(gè)application對(duì)象就是這個(gè)應(yīng)用程序的唯一入口了罚缕。WSGI還定義了application對(duì)象的形式:

def simple_app(environ, start_response):
      pass

上面代碼中的environstart_response就是server端調(diào)用application對(duì)象時(shí)傳遞的兩個(gè)參數(shù)。

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

使用標(biāo)準(zhǔn)庫(kù)(這個(gè)只是demo)

import wsgiref

application = wsgiref.simple_server.demo_app

使用web.py框架

import web

urls = (
    '/.*', 'hello',
)

class hello(object):
    def GET(self):
        return "Hello, world."

application = web.application(urls, globals()).wsgifunc()

你可以看到蚓聘,文件中都需要有一個(gè)application對(duì)象腌乡,server端會(huì)找到這個(gè)文件,然后調(diào)用這個(gè)對(duì)象夜牡。所以支持WSGI的Python框架最終都會(huì)有這么一個(gè)application對(duì)象导饲,不過框架的使用者不需要關(guān)心這個(gè)application對(duì)象內(nèi)部是如何工作的,只需要關(guān)心路由定義氯材、請(qǐng)求處理等具體的業(yè)務(wù)邏輯渣锦。

因?yàn)閍pplication對(duì)象是唯一的入口,所以不管客戶端請(qǐng)求的路徑和數(shù)據(jù)是什么氢哮,server都是調(diào)用這個(gè)application對(duì)象袋毙,具體的客戶端請(qǐng)求的處理有application對(duì)象完成。

application對(duì)象需要做什么

上面已經(jīng)提到了冗尤,application對(duì)象需要是一個(gè)可調(diào)用對(duì)象听盖,而且其定義需要滿足如下形式:

def simple_app(environ, start_response):
      pass

當(dāng)server按照WSGI的規(guī)范調(diào)用了application之后,application就可以開始處理客戶端的請(qǐng)求了裂七,處理請(qǐng)求之后皆看,application對(duì)象需要返回處理結(jié)果給server端。處理請(qǐng)求和返回結(jié)果這兩個(gè)事情背零,都和server調(diào)用application對(duì)象時(shí)傳遞的兩個(gè)參數(shù)有關(guān)腰吟。

environ參數(shù)

environ參數(shù)是一個(gè)Python的字典,里面存放了所有和客戶端相關(guān)的信息,這樣application對(duì)象就能知道客戶端請(qǐng)求的資源是什么毛雇,請(qǐng)求中帶了什么數(shù)據(jù)等嫉称。environ字典包含了一些CGI規(guī)范要求的數(shù)據(jù),以及WSGI規(guī)范新增的數(shù)據(jù)灵疮,還可能包含一些操作系統(tǒng)的環(huán)境變量以及Web服務(wù)器相關(guān)的環(huán)境變量织阅。我們來看一些environ中常用的成員:

首先是CGI規(guī)范中要求的變量:

  • REQUEST_METHOD: 請(qǐng)求方法,是個(gè)字符串震捣,'GET', 'POST'等

  • SCRIPT_NAME: HTTP請(qǐng)求的path中的用于查找到application對(duì)象的部分荔棉,比如Web服務(wù)器可以根據(jù)path的一部分來決定請(qǐng)求由哪個(gè)virtual host處理

  • PATH_INFO: HTTP請(qǐng)求的path中剩余的部分,也就是application要處理的部分

  • QUERY_STRING: HTTP請(qǐng)求中的查詢字符串蒿赢,URL中?后面的內(nèi)容

  • CONTENT_TYPE: HTTP headers中的content-type內(nèi)容

  • CONTENT_LENGTH: HTTP headers中的content-length內(nèi)容

  • SERVER_NAMESERVER_PORT: 服務(wù)器名和端口润樱,這兩個(gè)值和前面的SCRIPT_NAME, PATH_INFO拼起來可以得到完整的URL路徑

  • SERVER_PROTOCOL: HTTP協(xié)議版本,HTTP/1.0或者HTTP/1.1

  • HTTP_: 和HTTP請(qǐng)求中的headers對(duì)應(yīng)诉植。

WSGI規(guī)范中還要求environ包含下列成員:

  • wsgi.version:表示W(wǎng)SGI版本祥国,一個(gè)元組(1, 0)昵观,表示版本1.0

  • wsgi.url_scheme:http或者h(yuǎn)ttps

  • wsgi.input:一個(gè)類文件的輸入流晾腔,application可以通過這個(gè)獲取HTTP request body

  • wsgi.errors:一個(gè)輸出流,當(dāng)應(yīng)用程序出錯(cuò)時(shí)啊犬,可以將錯(cuò)誤信息寫入這里

  • wsgi.multithread:當(dāng)application對(duì)象可能被多個(gè)線程同時(shí)調(diào)用時(shí)灼擂,這個(gè)值需要為True

  • wsgi.multiprocess:當(dāng)application對(duì)象可能被多個(gè)進(jìn)程同時(shí)調(diào)用時(shí),這個(gè)值需要為True

  • wsgi.run_once:當(dāng)server期望application對(duì)象在進(jìn)程的生命周期內(nèi)只被調(diào)用一次時(shí)觉至,該值為True

上面列出的這些內(nèi)容已經(jīng)包括了客戶端請(qǐng)求的所有數(shù)據(jù)剔应,足夠application對(duì)象處理客戶端請(qǐng)求了。

start_resposne參數(shù)

start_response是一個(gè)可調(diào)用對(duì)象语御,接收兩個(gè)必選參數(shù)和一個(gè)可選參數(shù):

  • status: 一個(gè)字符串峻贮,表示HTTP響應(yīng)狀態(tài)字符串

  • response_headers: 一個(gè)列表,包含有如下形式的元組:(header_name, header_value)应闯,用來表示HTTP響應(yīng)的headers

  • exc_info(可選): 用于出錯(cuò)時(shí)纤控,server需要返回給瀏覽器的信息

當(dāng)application對(duì)象根據(jù)environ參數(shù)的內(nèi)容執(zhí)行完業(yè)務(wù)邏輯后,就需要返回結(jié)果給server端碉纺。我們知道HTTP的響應(yīng)需要包含status船万,headers和body,所以在application對(duì)象將body作為返回值return之前骨田,需要先調(diào)用start_response()耿导,將status和headers的內(nèi)容返回給server,這同時(shí)也是告訴server态贤,application對(duì)象要開始返回body了舱呻。

application對(duì)象的返回值

application對(duì)象的返回值用于為HTTP響應(yīng)提供body,如果沒有body悠汽,那么可以返回None狮荔。如果有body的化胎撇,那么需要返回一個(gè)可迭代的對(duì)象。server端通過遍歷這個(gè)可迭代對(duì)象可以獲得body的全部?jī)?nèi)容殖氏。

application demo

PEP-3333中有一個(gè)application的實(shí)現(xiàn)demo晚树,我把它再簡(jiǎn)化之后如下:

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

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

再談server如何調(diào)用application

前面已經(jīng)知道server如何定位到application的入口了雅采,也知道了application的入口的形式以及application對(duì)象內(nèi)部需要完成的工作爵憎。那么,我們還需要再說一下婚瓜,environstart_response()是需要在server端的生成和定義的宝鼓,其中關(guān)于start_response()的部分在規(guī)范中也有明確的要求。這部分內(nèi)容太長(zhǎng)了巴刻,不適合放在本文中愚铡,有興趣的讀者可以去看下PEP-3333,里面有一段server端的demo實(shí)現(xiàn)胡陪。

WSGI中間件

WSGI Middleware(中間件)也是WSGI規(guī)范的一部分沥寥。上一章我們已經(jīng)說明了WSGI的兩個(gè)角色:server和application。那么middleware是一種運(yùn)行在server和application中間的應(yīng)用(一般都是Python應(yīng)用)柠座。middleware同時(shí)具備server和application角色邑雅,對(duì)于server來說,它是一個(gè)application妈经;對(duì)于application來說淮野,它是一個(gè)server。middleware并不修改server端和application端的規(guī)范吹泡,只是同時(shí)實(shí)現(xiàn)了這兩個(gè)角色的功能而已骤星。

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

WSGI中間件

上圖中最上面的三個(gè)彩色框表示角色,中間的白色框表示操作爆哑,操作的發(fā)生順序按照1 ~ 5進(jìn)行了排序洞难,我們直接對(duì)著上圖來說明middleware是如何工作的:

  1. Server收到客戶端的HTTP請(qǐng)求后,生成了environ_s泪漂,并且已經(jīng)定義了start_response_s廊营。

  2. Server調(diào)用Middleware的application對(duì)象,傳遞的參數(shù)是environ_sstart_response_s萝勤。

  3. Middleware會(huì)根據(jù)environ執(zhí)行業(yè)務(wù)邏輯露筒,生成environ_m,并且已經(jīng)定義了start_response_m敌卓。

  4. Middleware決定調(diào)用Application的application對(duì)象慎式,傳遞參數(shù)是environ_mstart_response_m。Application的application對(duì)象處理完成后,會(huì)調(diào)用start_response_m并且返回結(jié)果給Middleware瘪吏,存放在result_m中癣防。

  5. Middleware處理result_m,然后生成result_s掌眠,接著調(diào)用start_response_s蕾盯,并返回結(jié)果result_s給Server端。Server端獲取到result_s后就可以發(fā)送結(jié)果給客戶端了蓝丙。

從上面的流程可以看出middleware應(yīng)用的幾個(gè)特點(diǎn):

  1. Server認(rèn)為middleware是一個(gè)application级遭。

  2. Application認(rèn)為middleware是一個(gè)server。

  3. Middleware可以有多層渺尘。

因?yàn)镸iddleware能過處理所有經(jīng)過的request和response挫鸽,所以要做什么都可以,沒有限制鸥跟。比如可以檢查request是否有非法內(nèi)容丢郊,檢查response是否有非法內(nèi)容,為request加上特定的HTTP header等医咨,這些都是可以的枫匾。

WSGI的實(shí)現(xiàn)和部署

要使用WSGI,需要分別實(shí)現(xiàn)server角色和application角色腋逆。

Application端的實(shí)現(xiàn)一般是由Python的各種框架來實(shí)現(xiàn)的婿牍,比如Django, web.py等侈贷,一般開發(fā)者不需要關(guān)心WSGI的實(shí)現(xiàn)惩歉,框架會(huì)會(huì)提供接口讓開發(fā)者獲取HTTP請(qǐng)求的內(nèi)容以及發(fā)送HTTP響應(yīng)。

Server端的實(shí)現(xiàn)會(huì)比較復(fù)雜一點(diǎn)俏蛮,這個(gè)主要是因?yàn)檐浖軜?gòu)的原因撑蚌。一般常用的Web服務(wù)器,如Apache和nginx搏屑,都不會(huì)內(nèi)置WSGI的支持争涌,而是通過擴(kuò)展來完成。比如Apache服務(wù)器辣恋,會(huì)通過擴(kuò)展模塊mod_wsgi來支持WSGI亮垫。Apache和mod_wsgi之間通過程序內(nèi)部接口傳遞信息,mod_wsgi會(huì)實(shí)現(xiàn)WSGI的server端伟骨、進(jìn)程管理以及對(duì)application的調(diào)用饮潦。Nginx上一般是用proxy的方式,用nginx的協(xié)議將請(qǐng)求封裝好携狭,發(fā)送給應(yīng)用服務(wù)器继蜡,比如uWSGI,應(yīng)用服務(wù)器會(huì)實(shí)現(xiàn)WSGI的服務(wù)端、進(jìn)程管理以及對(duì)application的調(diào)用稀并。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末仅颇,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子碘举,更是在濱河造成了極大的恐慌忘瓦,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件引颈,死亡現(xiàn)場(chǎng)離奇詭異政冻,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)线欲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門明场,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人李丰,你說我怎么就攤上這事苦锨。” “怎么了趴泌?”我有些...
    開封第一講書人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵舟舒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我嗜憔,道長(zhǎng)秃励,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任吉捶,我火速辦了婚禮夺鲜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘呐舔。我一直安慰自己币励,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開白布珊拼。 她就那樣靜靜地躺著食呻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪澎现。 梳的紋絲不亂的頭發(fā)上仅胞,一...
    開封第一講書人閱讀 49,985評(píng)論 1 291
  • 那天坠非,我揣著相機(jī)與錄音导匣,去河邊找鬼。 笑死策精,一個(gè)胖子當(dāng)著我的面吹牛揭斧,可吹牛的內(nèi)容都是我干的莱革。 我是一名探鬼主播峻堰,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼盅视!你這毒婦竟也來了捐名?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤闹击,失蹤者是張志新(化名)和其女友劉穎镶蹋,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赏半,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贺归,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了断箫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拂酣。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖仲义,靈堂內(nèi)的尸體忽然破棺而出婶熬,到底是詐尸還是另有隱情,我是刑警寧澤埃撵,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布赵颅,位于F島的核電站,受9級(jí)特大地震影響暂刘,放射性物質(zhì)發(fā)生泄漏饺谬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一谣拣、第九天 我趴在偏房一處隱蔽的房頂上張望募寨。 院中可真熱鬧,春花似錦芝发、人聲如沸绪商。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至腹殿,卻和暖如春独悴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锣尉。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工刻炒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人自沧。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓坟奥,卻偏偏與公主長(zhǎng)得像树瞭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子爱谁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

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