淺析使用Python搭建http服務器
David Wheeler有一句名言:“計算機科學中的任何問題,都可以通過加上另一層間接的中間層解決。”為了提高Python網(wǎng)絡服務的可移植性,Python社區(qū)在PEP 333中提出了Web服務器網(wǎng)關(guān)接口(WSGI,Web Server Gateway Interface)。
為了提高Python網(wǎng)絡服務的可移植性,Python社區(qū)在PEP 333中提出了Web服務器網(wǎng)關(guān)接口(WSGI,Web Server Gateway Interface)。
WSGL標準就是添加了一層中間層。通過這一個中間層,用Python編寫的HTTP服務就能夠與任何Web服務器進行交互了?,F(xiàn)在,WSGI已經(jīng)成為了使用Python進行HTTP操作的標準方法。
按照標準的定義,WSGI應用程序是可以被調(diào)用的,并且有兩個輸入?yún)?shù)。
1、WSGI
下面是第一段代碼,第一個參數(shù)是environ,用于接收一個字典,字典中提供的鍵值對是舊式的CGI環(huán)境集合的拓展。第二個參數(shù)本身也是可以被調(diào)用的,習慣上會將其命名為start_response(),WSGI應用程序通過這個參數(shù)來聲明響應頭信息。
# 用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服務器,用于接收請求,然后對接收到的請求(至少是部分請求)進行轉(zhuǎn)發(fā)。轉(zhuǎn)發(fā)請求時代理會扮演客戶端的角色,將轉(zhuǎn)發(fā)的HTTP請求發(fā)送至真正的服務器,最后將從服務器接受到的響應發(fā)揮扮演客戶端的角色,將轉(zhuǎn)發(fā)的請求發(fā)送至真正的服務器,最后將從服務器接受到的響應發(fā)回給最初的客戶端。
下面是前向代理和反向代理的簡圖。
反向代理已經(jīng)廣泛應用于大型的HTTP服務當中。反向代理是Web服務的一部分,對于HTTP客戶端并不可見。
3、四種架構(gòu)
架構(gòu)師一般都使用很多種復雜的機制來將多個子模塊組合建成一個HTTP服務?,F(xiàn)在在Python社區(qū)中,已經(jīng)形成了4種基本的模式。如果已經(jīng)編寫了用于生成動態(tài)內(nèi)容的Python代碼,并且已經(jīng)選擇了某個支持WSGI的API或框架,應該如何將HTTP服務部署到線上呢?
運行一個使用Python編寫的服務器,服務器的代碼中可以直接調(diào)用WSGI接口?,F(xiàn)在最流行的是Green Unicorn(Gunicorn)服務器,不過也有其他已經(jīng)可以用于生產(chǎn)環(huán)境的純Python服務器。
配置mod_wsgi并運行Apache,在一個獨立的WSFIDaemonProcess中運行Python代碼,由mod_wsgi啟動守護進程。
在后端運行一個類似于Gunicorn的Python HTTP服務器(或者支持所選異步框架的任何服務器),然后在前端運行一個既能返回靜態(tài)文件,又能對Python編寫的動態(tài)資源服務進行反向代理的Web服務器。
在最前端運行一個純粹的反向代理(如Varnish),在該反向代理后端運行Apache或者nginx,在后端運行Python編寫的HTTP服務器。這是一個三層的架構(gòu)。這些反向代理可以分布在不同的地理位置,這樣子就能夠?qū)㈦x客戶端最近的反向代理上的緩存資源返回給發(fā)送請求的客戶端。
長期以來,對這4個架構(gòu)的選擇主要基于CPython的3個運行時的特性,即解釋器占用內(nèi)存大、解釋器運行慢、全局解釋器(GIL,Global Interpreter Lock)禁止多個線程同時運行Python字節(jié)碼。但同時帶來了內(nèi)存中只能載入一定數(shù)量的Python實例。
4、平臺即服務
這個概念的出現(xiàn)是因為現(xiàn)在的自動化部署、持續(xù)集成以及高性能大規(guī)模服務的相關(guān)技術(shù)的出現(xiàn)和處理有一些繁雜。所以有一些提供商提出了PaaS(Platform as a Service),現(xiàn)在只需關(guān)心應該如何打包自己的應用程序,以便將自己的應用部署到這些服務之上。
PaaS提供商會解決構(gòu)建和運行HTTP服務中的出現(xiàn)的各種煩心事。不需要再關(guān)心服務器,或者是提供IP地址之類的事情。
PaaS會根據(jù)客戶規(guī)模提供負載均衡器。只需要給PaaS提供商提供配置文件即可完成各種復雜的步驟。
現(xiàn)階段比較常用的有Heroku和Docker。
大多數(shù)PaaS提供商不支持靜態(tài)內(nèi)容,除非我們在Python應用程序中實現(xiàn)了對靜態(tài)內(nèi)容的更多支持或者向容器中加入了Apache或ngnix。盡管我們可以將靜態(tài)資源和動態(tài)頁面的路徑放在兩個完全不同的URL空間內(nèi),但是許多架構(gòu)師還是傾向于將兩者放在同一個名字空間內(nèi)。
5、不使用Web框架編寫WSGI可調(diào)用對象
下面第一段代碼是用于返回當前時間的原始WSGI可調(diào)用對象。
#!/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編寫的可調(diào)用對象返回當前時間。
#!/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可調(diào)用對象返回當前時間。
#!/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框架的基礎(chǔ)。
總結(jié)
以上所述是小編給大家介紹的使用Python搭建http服務器,希望對大家有所幫助!
相關(guān)文章
Django利用AJAX技術(shù)實現(xiàn)博文實時搜索
這篇文章主要介紹了Django如何利用AJAX技術(shù)實現(xiàn)博文實時搜索,幫助大家更好的理解和學習使用Django框架,感興趣的朋友可以了解下2021-05-05Python利用numpy實現(xiàn)三層神經(jīng)網(wǎng)絡的示例代碼
這篇文章主要介紹了Python利用numpy實現(xiàn)三層神經(jīng)網(wǎng)絡的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04Python自動化短視頻生成腳本實現(xiàn)熱門視頻流水線生產(chǎn)
有粉絲和說,最近在網(wǎng)上看到一些視頻營銷號一天能發(fā)布幾百條短視頻, 感覺是批量生成的,能不能用Python做個自動化短視頻生成腳本呢?今天就帶大家一起實現(xiàn)熱門視頻批量流水線生產(chǎn)2021-09-09C++通過內(nèi)嵌解釋器調(diào)用Python及間接調(diào)用Python三方庫
本文主要介紹了C++通過內(nèi)嵌解釋器調(diào)用Python及間接調(diào)用Python三方庫,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12關(guān)于sys.stdout和print的區(qū)別詳解
今天小編就為大家分享一篇關(guān)于sys.stdout和print的區(qū)別詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12Python通過fnmatch模塊實現(xiàn)文件名匹配
這篇文章主要介紹了Python通過fnmatch模塊實現(xiàn)文件名匹配,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-09-09