您好,登錄后才能下訂單哦!
David Wheeler有一句名言:“計算機科學中的任何問題,都可以通過加上另一層間接的中間層解決。”為了提高Python網絡服務的可移植性,Python社區在PEP 333中提出了Web服務器網關接口(WSGI,Web Server Gateway Interface)。 |
為了提高Python網絡服務的可移植性,Python社區在PEP 333中提出了Web服務器網關接口(WSGI,Web Server Gateway Interface)。
WSGL標準就是添加了一層中間層。通過這一個中間層,用Python編寫的HTTP服務就能夠與任何Web服務器進行交互了。現在,WSGI已經成為了使用Python進行HTTP操作的標準方法。
按照標準的定義,WSGI應用程序是可以被調用的,并且有兩個輸入參數。
1、WSGI
下面是第一段代碼,第一個參數是environ,用于接收一個字典,字典中提供的鍵值對是舊式的CGI環境集合的拓展。第二個參數本身也是可以被調用的,習慣上會將其命名為start_response(),WSGI應用程序通過這個參數來聲明響應頭信息。
# 用WSGI應用形式編寫的簡單HTTP服務。
#!/usr/bin/env?python3? #?A?simple?HTTP?service?built?directly?against?the?low-level?WSGI?spec.? ? from?pprint?import?pformat? from?wsgiref.simple_server?import?make_server? ? def?app(environ,?start_response):? ????headers?=?{'Content-Type':?'text/plain;?charset=utf-8'}? ????start_response('200?OK',?list(headers.items()))? ????yield?'Here?is?the?WSGI?environment:? ? '.encode('utf-8')? ????yield?pformat(environ).encode('utf-8')? ? if?__name__?==?'__main__':? ????httpd?=?make_server('',?8000,?app)? ????host,?port?=?httpd.socket.getsockname()? ????print('Serving?on',?host,?'port',?port)? ????httpd.serve_forever()
上述只是一個簡單的情況。但是在編寫服務器程序時,復雜度就大大提升了。這是因為要完全考慮標準中的描述的許多注意點和邊界情況。
2、前向代理與反向代理
無論前向代理還是反向代理,HTTP代理其實就是一個HTTP服務器,用于接收請求,然后對接收到的請求(至少是部分請求)進行轉發。轉發請求時代理會扮演客戶端的角色,將轉發的HTTP請求發送至真正的服務器,最后將從服務器接受到的響應發揮扮演客戶端的角色,將轉發的請求發送至真正的服務器,最后將從服務器接受到的響應發回給最初的客戶端。
下面是前向代理和反向代理的簡圖。
反向代理已經廣泛應用于大型的HTTP服務當中。反向代理是Web服務的一部分,對于HTTP客戶端并不可見。
3、四種架構
架構師一般都使用很多種復雜的機制來將多個子模塊組合建成一個HTTP服務。現在在Python社區中,已經形成了4種基本的模式。如果已經編寫了用于生成動態內容的Python代碼,并且已經選擇了某個支持WSGI的API或框架,應該如何將HTTP服務部署到線上呢?
運行一個使用Python編寫的服務器,服務器的代碼中可以直接調用WSGI接口。現在最流行的是Green Unicorn(Gunicorn)服務器,不過也有其他已經可以用于生產環境的純Python服務器。
配置mod_wsgi并運行Apache,在一個獨立的WSFIDaemonProcess中運行Python代碼,由mod_wsgi啟動守護進程。
在后端運行一個類似于Gunicorn的Python HTTP服務器(或者支持所選異步框架的任何服務器),然后在前端運行一個既能返回靜態文件,又能對Python編寫的動態資源服務進行反向代理的Web服務器。
在最前端運行一個純粹的反向代理(如Varnish),在該反向代理后端運行Apache或者nginx,在后端運行Python編寫的HTTP服務器。這是一個三層的架構。這些反向代理可以分布在不同的地理位置,這樣子就能夠將離客戶端最近的反向代理上的緩存資源返回給發送請求的客戶端。
長期以來,對這4個架構的選擇主要基于CPython的3個運行時的特性,即解釋器占用內存大、解釋器運行慢、全局解釋器(GIL,Global Interpreter Lock)禁止多個線程同時運行Python字節碼。但同時帶來了內存中只能載入一定數量的Python實例。
4、平臺即服務
這個概念的出現是因為現在的自動化部署、持續集成以及高性能大規模服務的相關技術的出現和處理有一些繁雜。所以有一些提供商提出了PaaS(Platform as a Service),現在只需關心應該如何打包自己的應用程序,以便將自己的應用部署到這些服務之上。
PaaS提供商會解決構建和運行HTTP服務中的出現的各種煩心事。不需要再關心服務器,或者是提供IP地址之類的事情。
PaaS會根據客戶規模提供負載均衡器。只需要給PaaS提供商提供配置文件即可完成各種復雜的步驟。
現階段比較常用的有Heroku和Docker。
大多數PaaS提供商不支持靜態內容,除非我們在Python應用程序中實現了對靜態內容的更多支持或者向容器中加入了Apache或ngnix。盡管我們可以將靜態資源和動態頁面的路徑放在兩個完全不同的URL空間內,但是許多架構師還是傾向于將兩者放在同一個名字空間內。
5、不使用Web框架編寫WSGI可調用對象
下面第一段代碼是用于返回當前時間的原始WSGI可調用對象。
#!/usr/bin/env?python3? #?A?simple?HTTP?service?built?directly?against?the?low-level?WSGI?spec.? ? import?time? ? def?app(environ,?start_response):? ????host?=?environ.get('HTTP_HOST',?'127.0.0.1')? ????path?=?environ.get('PATH_INFO',?'/')? ????if?':'?in?host:? ????????host,?port?=?host.split(':',?1)? ????if?'?'?in?path:? ????????path,?query?=?path.split('?',?1)? ????headers?=?[('Content-Type',?'text/plain;?charset=utf-8')]? ????if?environ['REQUEST_METHOD']?!=?'GET':? ????????start_response('01?Not?Implemented',?headers)? ????????yield?b'01?Not?Implemented'? ????elif?host?!=?'127.0.0.1'?or?path?!=?'/':? ????????start_response('404?Not?Found',?headers)? ????????yield?b'404?Not?Found'? ????else:? ????????start_response('200?OK',?headers)? ????????yield?time.ctime().encode('ascii')
第一段比較冗長。下面使用第三方庫簡化原始WGSI的模式方法。
第一個示例是使用WebOb編寫的可調用對象返回當前時間。
#!/usr/bin/env?python3? #?A?WSGI?callable?built?using?webob.? ? import?time,?webob? ? def?app(environ,?start_response):? ????request?=?webob.Request(environ)? ????if?environ['REQUEST_METHOD']?!=?'GET':? ????????response?=?webob.Response('501?Not?Implemented',?status=501)? ????elif?request.domain?!=?'127.0.0.1'?or?request.path?!=?'/':? ????????response?=?webob.Response('404?Not?Found',?status=404)? ????else:? ????????response?=?webob.Response(time.ctime())? ????return?response(environ,?start_response)
第二個是使用Werkzeug編寫的WSGI可調用對象返回當前時間。
#!/usr/bin/env?python3? #?A?WSGI?callable?built?using?Werkzeug.? ? import?time? from?werkzeug.wrappers?import?Request,?Response? ? @Request.application? def?app(request):? ????host?=?request.host? ????if?':'?in?host:? ????????host,?port?=?host.split(':',?1)? ????if?request.method?!=?'GET':? ????????return?Response('501?Not?Implemented',?status=501)? ????elif?host?!=?'127.0.0.1'?or?request.path?!=?'/':? ????????return?Response('404?Not?Found',?status=404)? ????else:? ????????return?Response(time.ctime())
大家可以對比這兩個庫在簡化操作時的不同之處,Werkzeug是Flask框架的基礎。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。