Python核心編程筆記————Web 編程:CGI 和 WSGI(三)

WCGI

動機

. CGI的目的是爲了動態的創建內容,但是這種方式無法擴展,CGI進程針對每個請求進行創建(就像是打開Python解釋器),用完就拋棄,如果接受了上千個請求,創建大量的語言解釋器進程很快就會導致服務器停機。有兩種方法來解決這個問題:服務器集成和外部進程

服務器集成

. 也稱服務器API。當前應用最廣泛的服務器解決方案是Apache HTTP Web 服務器,這是一個開源的解決方案,通常稱爲 Apache,擁有一個服務器 API。
  這類解決方案將網關集成進服務器。換句話說不是將服務器切分成多個語言解釋器來分別處理請求,而是生成函數調用,運行應用程序代碼,在運行過程中進行響應。
  其缺點也有很多,如:含有bug的代碼會影響服務器實現的執行效率、不同語言的實現無法完全兼容,應用程序必須是線程安全的等等。

外部進程

. 這種方案是讓CGI應用在服務器外部運行,當有請求時,服務器將請求傳遞到外部進程中。外部進程的存在時間很長,不會再處理玩請求後就結束。使用外部進程最廣爲人知的方案是FastCGI,外部進程相比服務器集成是更優的選擇。
  同時,爲了降低開發者尤其是Web礦建開發者的負擔,建立了Web服務器網類接口(Web Server Gateway Interface,WSGI)標準。

WSGI簡介

. WSGI規範創建於2003年,用於處理日益增多的不同的Web框架、Web服務器,及其它調用方式。
  根據WSGI定義,其應用是可調用的對象,其參數固定爲以下兩個:一個是含有服務器環境變量的字典,另一個是可調用對象,該對象使用HTTP狀態碼和會返回給客戶端的HTTP頭來初始化響應。其必須返回一個可迭代對象用於組成響應負載。以下是一個簡單的示例:

def simple_wsgi_app(environ, start_response):
	status = '200 OK'
	headers = [('Content-type', 'text/plain')]
	start_response(status, headers)
	return ['Hello world!']

. start_response()這個可調用對象必須在應用執行,生成最終會發送回客戶端的響應。響應必須含有 HTTP 返回碼(200、300 等),以及 HTTP 響應頭。另外返回的內容必須是可迭代的,如列表、生成器等,他們生成實際的響應負載。
  start_response()還有一個可選的第三參數(exc_info),這個參數含有異常信息。應用剛開始使用正常的一對參數執行,當發生錯誤的時候,會再次調用start_response(),但會將新的狀態碼、http頭和exc_info一起傳入,替換原有的內容。如果第二次調用時沒有提供exc_info,則會發生錯誤。

參考服務器

. Python在標準庫中提供了簡單的參考服務器:wsgiref.simple_server.WSGIServer
  可以用這個類直接構建一個服務器。wsgiref包還提供了一個方便的函數:make_server(),通過這個函數可部署一個用於簡單訪問的參考服務器,下面是示例:

from wsgiref.simple_server import make_server

def simple_wsgi_app(environ, start_response):
    status = '200 OK'
    headers = [('Content-type', 'text/plain')]
    start_response(status, headers)
    return [u'Hello world!'.encode('utf8')]			#這裏不encode會報錯


httpd = make_server('',8000,simple_wsgi_app)
print('started app serving on port 8000...')
httpd.serve_forever()

. 運行這個模塊後,在瀏覽器訪問http://localhost:8000可以看到純文本形式的Hello world!。其實wsgiref 模塊還有一個示例應用demo_app(),可以導入demo_app,並將make_server的第三個參數換成demo_app()看看有什麼不同。

中間件機封裝WSGI應用

. 在某些情況下,除了應用程序本身之外,還希望在應用程序執行之前或是執行之後添加一些處理程序,即中間件。其要麼對來自客戶的數據進行預處理,然後發送給應用,要麼在應用將響應負載發送給用戶之前,對結果數據進行一些最終的調整。
  下面的例子是爲之前的simple_wsgi_app的返回結果打上時間戳,並且使用類封裝的方式,使參數整合到一個元組變量中,減少代碼量:

from wsgiref.simple_server import make_server
from time import ctime

def simple_wsgi_app(environ, start_response):
    status = '200 OK'
    headers = [('Content-type', 'text/plain')]
    start_response(status, headers)
    return ['Hello world!\n','nice']			#這裏和之前不一樣,沒有先encode了


class Ts_ci_wrapp(object):
    def __init__(self,app):
        self.orig_app = app

    def __call__(self,*stuff):
        return (('[%s] %s' % (ctime(),x)).encode() for x in self.orig_app(*stuff))

httpd = make_server('',8000,Ts_ci_wrapp(simple_wsgi_app))
print('started app serving on port 8000...')
httpd.serve_forever()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章