使用Python搭建http服務器

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應用程序是能夠被調用的,而且有兩個輸入參數。前端

一、WSGIpython

下面是第一段代碼,第一個參數是environ,用於接收一個字典,字典中提供的鍵值對是舊式的CGI環境集合的拓展。第二個參數自己也是能夠被調用的,習慣上會將其命名爲start_response(),WSGI應用程序經過這個參數來聲明響應頭信息。
# 用WSGI應用形式編寫的簡單HTTP服務。nginx

#!/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()

上述只是一個簡單的狀況。可是在編寫服務器程序時,複雜度就大大提高了。這是由於要徹底考慮標準中的描述的許多注意點和邊界狀況。web

二、前向代理與反向代理後端

不管前向代理仍是反向代理,HTTP代理其實就是一個HTTP服務器,用於接收請求,而後對接收到的請求(至少是部分請求)進行轉發。轉發請求時代理會扮演客戶端的角色,將轉發的HTTP請求發送至真正的服務器,最後將從服務器接受到的響應發揮扮演客戶端的角色,將轉發的請求發送至真正的服務器,最後將從服務器接受到的響應發回給最初的客戶端。
下面是前向代理和反向代理的簡圖。
使用Python搭建http服務器使用Python搭建http服務器
反向代理已經普遍應用於大型的HTTP服務當中。反向代理是Web服務的一部分,對於HTTP客戶端並不可見。緩存

三、四種架構服務器

架構師通常都使用不少種複雜的機制來將多個子模塊組合建成一個HTTP服務。如今在Python社區中,已經造成了4種基本的模式。若是已經編寫了用於生成動態內容的Python代碼,而且已經選擇了某個支持WSGI的API或框架,應該如何將HTTP服務部署到線上呢?網絡

運行一個使用Python編寫的服務器,服務器的代碼中能夠直接調用WSGI接口。如今最流行的是Green Unicorn(Gunicorn)服務器,不過也有其餘已經能夠用於生產環境的純Python服務器。架構

配置mod_wsgi並運行Apache,在一個獨立的WSFIDaemonProcess中運行Python代碼,由mod_wsgi啓動守護進程。app

在後端運行一個相似於Gunicorn的Python HTTP服務器(或者支持所選異步框架的任何服務器),而後在前端運行一個既能返回靜態文件,又能對Python編寫的動態資源服務進行反向代理的Web服務器。

在最前端運行一個純粹的反向代理(如Varnish),在該反向代理後端運行Apache或者nginx,在後端運行Python編寫的HTTP服務器。這是一個三層的架構。這些反向代理能夠分佈在不一樣的地理位置,這樣子就可以將離客戶端最近的反向代理上的緩存資源返回給發送請求的客戶端。

使用Python搭建http服務器使用Python搭建http服務器
長期以來,對這4個架構的選擇主要基於CPython的3個運行時的特性,即解釋器佔用內存大、解釋器運行慢、全局解釋器(GIL,Global Interpreter Lock)禁止多個線程同時運行Python字節碼。但同時帶來了內存中只能載入必定數量的Python實例。

四、平臺即服務

這個概念的出現是由於如今的自動化部署、持續集成以及高性能大規模服務的相關技術的出現和處理有一些繁雜。因此有一些提供商提出了PaaS(Platform as a Service),如今只需關心應該如何打包本身的應用程序,以便將本身的應用部署到這些服務之上。
PaaS提供商會解決構建和運行HTTP服務中的出現的各類煩心事。不須要再關心服務器,或者是提供IP地址之類的事情。
PaaS會根據客戶規模提供負載均衡器。只須要給PaaS提供商提供配置文件便可完成各類複雜的步驟。
現階段比較經常使用的有Heroku和Docker。
大多數PaaS提供商不支持靜態內容,除非咱們在Python應用程序中實現了對靜態內容的更多支持或者向容器中加入了Apache或ngnix。儘管咱們能夠將靜態資源和動態頁面的路徑放在兩個徹底不一樣的URL空間內,可是許多架構師仍是傾向於將二者放在同一個名字空間內。

五、不使用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框架的基礎。

相關文章
相關標籤/搜索