使用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應用程式是可以被呼叫的,並且有兩個輸入引數。
下面是第一段程式碼,第一個引數是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()
上述只是一個簡單的情況。但是在編寫伺服器程式時,複雜度就大大提升了。這是因為要完全考慮標準中的描述的許多注意點和邊界情況。
無論前向代理還是反向代理,HTTP代理其實就是一個HTTP伺服器,用於接收請求,然後對接收到的請求(至少是部分請求)進行轉發。轉發請求時代理會扮演客戶端的角色,將轉發的HTTP請求傳送至真正的伺服器,最後將從伺服器接受到的響應發揮扮演客戶端的角色,將轉發的請求傳送至真正的伺服器,最後將從伺服器接受到的響應發回給最初的客戶端。
下面是前向代理和反向代理的簡圖。
反向代理已經廣泛應用於大型的HTTP服務當中。反向代理是Web服務的一部分,對於HTTP客戶端並不可見。
架構師一般都使用很多種複雜的機制來將多個子模組組合建成一個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例項。
這個概念的出現是因為現在的自動化部署、持續整合以及高效能大規模服務的相關技術的出現和處理有一些繁雜。所以有一些提供商提出了PaaS(Platform as a Service),現在只需關心應該如何打包自己的應用程式,以便將自己的應用部署到這些服務之上。
PaaS提供商會解決構建和執行HTTP服務中的出現的各種煩心事。不需要再關心伺服器,或者是提供IP地址之類的事情。
PaaS會根據客戶規模提供負載均衡器。只需要給PaaS提供商提供配置檔案即可完成各種複雜的步驟。
現階段比較常用的有Heroku和Docker。
大多數PaaS提供商不支援靜態內容,除非我們在Python應用程式中實現了對靜態內容的更多支援或者向容器中加入了Apache或ngnix。儘管我們可以將靜態資源和動態頁面的路徑放在兩個完全不同的URL空間內,但是許多架構師還是傾向於將兩者放在同一個名字空間內。
下面第一段程式碼是用於返回當前時間的原始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('501 Not Implemented', headers) yield b'501 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框架的基礎。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31524109/viewspace-2661564/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 在伺服器上使用 smart http 搭建 Git 伺服器伺服器HTTPGit
- 用Python快速搭建http伺服器和FTP伺服器的步驟PythonHTTP伺服器FTP
- 使用Python建立簡單的HTTP伺服器PythonHTTP伺服器
- RHEL9.4上使用apache搭建http伺服器提供repo源ApacheHTTP伺服器
- python http服務怎麼搭建PythonHTTP
- IIS搭建的http檔案伺服器HTTP伺服器
- netty系列之:搭建客戶端使用http1.1的方式連線http2伺服器Netty客戶端HTTP伺服器
- 網路通訊6:搭建HTTP伺服器HTTP伺服器
- Node.js 系列 - 搭建 "Hello World" HTTP 伺服器Node.jsHTTP伺服器
- Node.js 系列 – 搭建 “Hello World” HTTP 伺服器Node.jsHTTP伺服器
- netty系列之:搭建HTTP上傳檔案伺服器NettyHTTP伺服器
- 學生黨自學Python:1分鐘搭建HTTP伺服器並實現遠端下載PythonHTTP伺服器
- 使用nodejs和express搭建http web服務NodeJSExpressHTTPWeb
- 使用 Java 11 HTTP Client API 實現 HTTP/2 伺服器推送JavaHTTPclientAPI伺服器
- java搭建http代理伺服器詳細教程(含程式碼)JavaHTTP伺服器
- Nodejs快速搭建簡單的HTTP伺服器詳細教程。NodeJSHTTP伺服器
- 基於Apache搭建HTTP HTTPS 正向代理 反向代理伺服器ApacheHTTP伺服器
- 使用MCSManager搭建Minecraft伺服器Raft伺服器
- FTP伺服器——使用vsftpd搭建FTP伺服器
- 使用python搭建伺服器並實現Android端與之通訊Python伺服器Android
- 用 Python 快速實現 HTTP 和 FTP 伺服器PythonHTTPFTP伺服器
- docker使用nginx搭建靜伺服器DockerNginx伺服器
- 使用solr搭建搜尋伺服器Solr伺服器
- Python 臨時啟動簡單的 HTTP 伺服器PythonHTTP伺服器
- 使用Java Socket手擼一個http伺服器JavaHTTP伺服器
- 使用 HTTP2 做開發伺服器 (上)HTTP伺服器
- 使用Nginx搭建流媒體伺服器Nginx伺服器
- 使用伺服器docker搭建Pwn題目伺服器Docker
- Windows下使用GitStack搭建Git伺服器WindowsGit伺服器
- 使用樹莓派搭建Ubuntu伺服器樹莓派Ubuntu伺服器
- 使用Nginx搭建公網代理伺服器Nginx伺服器
- centOS中搭建nginx,並使用letsencrypt配置http/2.0(part 1)CentOSNginxHTTP
- node之搭建一個http完整的靜態伺服器(命令列工具)HTTP伺服器命令列
- http代理伺服器HTTP伺服器
- 使用Python獲取HTTP請求頭資料PythonHTTP
- ubuntu系統使用vsftpd搭建FTP伺服器。UbuntuFTP伺服器
- 使用FRP、雲伺服器搭建內網穿透FRP伺服器內網穿透
- 使用Dnsmasq搭建本地dns伺服器上網DNS伺服器