Django3 --- ASGI
1. 什么是WSGI
1.1 CGI
解釋 WSGI 之前應(yīng)該先說一下什么是 CGI(通用網(wǎng)關(guān)接口,Common Gateway Interface,CGI),是Web 服務(wù)器運(yùn)行時外部程序的規(guī)范 , 是外部擴(kuò)展應(yīng)用程序與 Web 服務(wù)器交互的一個標(biāo)準(zhǔn)接口。 CGI規(guī)范定義了Web服務(wù)器如何向擴(kuò)展應(yīng)用程序發(fā)送消息,在收到擴(kuò)展應(yīng)用程序的信息后又如何進(jìn)行處理等內(nèi)容。對于許多靜態(tài)的HTML網(wǎng)頁無法實現(xiàn)的功能,通過 CGI可以實現(xiàn),比如表單的處理、對數(shù)據(jù)庫的訪問、搜索引擎、基于Web的數(shù)據(jù)庫訪問等等。使用CGI實現(xiàn)客戶端與服務(wù)器的交互有以下幾個標(biāo)準(zhǔn)步驟 :
(1)Web 客戶端的瀏覽器將URL的第一部分解碼與Web服務(wù)器相連。
(2)Web 瀏覽器將URL的其余部分提供給服務(wù)器。
(3)Web 服務(wù)器將URL轉(zhuǎn)換成路徑和文件名。
(4)Web 服務(wù)器發(fā)送 HTML 和別的組成請求頁面的文件給客戶。一旦頁面內(nèi)容傳送完,這個連接自動斷開。
(5)在客戶端,HTML腳本提示用戶做動作或輸入。當(dāng)用戶響應(yīng)后,客戶請求Web服務(wù)器建立一個新的連接。
(6)Web 服務(wù)器把這些信息和別的進(jìn)程變量傳送給由HTML以URL的形式指定CGI程序。
(7)CGI 根據(jù)輸入作出響應(yīng),把響應(yīng)結(jié)果傳送給 Web 服務(wù)器。
(8)Web 服務(wù)器把響應(yīng)的數(shù)據(jù)傳給客戶,完成后關(guān)閉連接。
1.2 WSGI
WSGI (Web服務(wù)網(wǎng)關(guān)接口,Python Web Server Gateway Interface,縮寫為WSGI) 是為Python語言定義的Web服務(wù)器和Web應(yīng)用程序或框架之間的一種簡單而通用的接口 。 WSGI 沒有官方的實現(xiàn), 因為WSGI更像一個協(xié)議。只要遵照這些協(xié)議,WSGI應(yīng)用(Application)都可以在任何服務(wù)器(Server)上運(yùn)行 。 它是作為Web服務(wù)器與Web應(yīng)用程序或應(yīng)用框架之間的一種低級別的接口,以提升可移植Web應(yīng)用開發(fā)的共同點。WSGI是基于現(xiàn)存的CGI標(biāo)準(zhǔn)而設(shè)計的 。
實現(xiàn)了WSGI 協(xié)議的 服務(wù)器有:uWSGI、uvicorn、gunicorn。像Django框架生產(chǎn)環(huán)境一般就不會使用runserver來運(yùn)行,而是采用上面實現(xiàn)了WSGI協(xié)議的服務(wù)器來運(yùn)行。
Django 中運(yùn)行 runserver 命令時,其實內(nèi)部就啟動了wsgiref模塊作為Web服務(wù)器運(yùn)行的,它的性能比較低下。
我的博客:nginx+uWSGI + django部署項目一篇中也有介紹。
1.3 Web服務(wù)器
Web服務(wù)器(Web Server)是一種運(yùn)行于網(wǎng)站后臺(物理服務(wù)器)的軟件。Web服務(wù)器主要用于提供網(wǎng)頁瀏覽或文件下載服務(wù),它可以向瀏覽器等Web客戶端提供html網(wǎng)頁文檔,也可以提供其他類型的可展示文檔,讓客戶端用戶瀏覽;還可以提供數(shù)據(jù)下載等,
目前業(yè)內(nèi)主流的Web服務(wù)器有Nginx、Apache、IIS、Tomcat。
1.4 Web應(yīng)用程序
上圖寫作Python程序
Web應(yīng)用程序是一種能完成Web業(yè)務(wù)邏輯,能讓用戶基于Web瀏覽器訪問的應(yīng)用程序,它可以是一個實現(xiàn)http請求和響應(yīng)功能的函數(shù)或者類,也可以是Django、Flask、tornado等這樣的web框架,當(dāng)然,也可以是其他語言的Web程序或Web框架。
Web服務(wù)器和Web應(yīng)用程序的區(qū)別:
- Web應(yīng)用程序主要是完成Web應(yīng)用業(yè)務(wù)邏輯的處理;
- Web服務(wù)器則主要應(yīng)對外部請求的接收、響應(yīng)、和轉(zhuǎn)發(fā)。
需要使用Web服務(wù)器啟動運(yùn)行,Web應(yīng)用程序才能倍用戶訪問到。
而Django框架中我們之所以只有一個Web應(yīng)用程序就跑起來,是因為我們在終端執(zhí)行了一個命令,python manage.py runserver 。這個命令啟動了Django框架中內(nèi)置提供的測試Web服務(wù)器(這個內(nèi)置服務(wù)器功能較差)。
2. 什么是ASGI
ASGI(異步服務(wù)器網(wǎng)關(guān)接口)是 WSGI 的繼承者,旨在**提供具有異步能力的 Python Web 服務(wù)器、框架和應(yīng)用程序之間的標(biāo)準(zhǔn)接口。 **
ASGI 被構(gòu)造為一個單一的、異步的可調(diào)用對象。它需要一個scope,它dict包含有關(guān)特定連接的詳細(xì)信息 send,一個異步可調(diào)用對象,它允許應(yīng)用程序向客戶端發(fā)送事件消息,以及receive一個異步可調(diào)用對象,它允許應(yīng)用程序從客戶端接收事件消息。
這不僅允許每個應(yīng)用程序有多個傳入事件和傳出事件,而且還允許后臺協(xié)程,以便應(yīng)用程序可以做其他事情(例如偵聽外部觸發(fā)器上的事件,如 Redis 隊列)。
以最簡單的形式,應(yīng)用程序可以編寫為異步函數(shù),如下所示:
async def application(scope, receive, send):event = await receive()...await send({"type": "websocket.send", ...})您發(fā)送或接收的每個事件都是一個 Python dict,具有預(yù)定義的格式。正是這些事件格式構(gòu)成了標(biāo)準(zhǔn)的基礎(chǔ),并允許應(yīng)用程序在服務(wù)器之間進(jìn)行交換。
這些事件每個都有一個定義的type鍵,可用于推斷事件的結(jié)構(gòu)。以下是您可能receive從 HTTP 請求的正文中接收到的示例事件 :
{"type": "http.request","body": b"Hello World","more_body": False, }這是您可能傳遞send給發(fā)送傳出 WebSocket 消息的事件示例:
{"type": "websocket.send","text": "Hello world!", }2.1 ASGI 規(guī)范
2.1.1 抽象的
網(wǎng)絡(luò)協(xié)議服務(wù)器(尤其是 Web 服務(wù)器)和 Python 應(yīng)用程序之間的標(biāo)準(zhǔn)接口,旨在允許處理多種常見的協(xié)議樣式(包括 HTTP、HTTP/2 和 WebSocket)。
這個基本規(guī)范旨在修復(fù)這些服務(wù)器交互和運(yùn)行應(yīng)用程序代碼的 API 集;每個支持的協(xié)議(例如 HTTP)都有一個子規(guī)范,概述了如何將該協(xié)議編碼和解碼為消息。
2.1.2 基本原理
WSGI 規(guī)范自推出以來一直運(yùn)行良好,并為 Python 框架和 Web 服務(wù)器選擇提供了極大的靈活性。然而,它的設(shè)計不可撤銷地與 HTTP 風(fēng)格的請求/響應(yīng)周期相關(guān)聯(lián),越來越多的不遵循這種模式的協(xié)議正在成為 Web 編程的標(biāo)準(zhǔn)部分(最顯著的是 WebSocket)。
ASGI 試圖保留一個簡單的應(yīng)用程序接口,同時提供一個抽象,允許隨時從不同的應(yīng)用程序線程或進(jìn)程發(fā)送和接收數(shù)據(jù)。
它還采用將協(xié)議轉(zhuǎn)換為 Python 兼容、異步友好的消息集的原則,并將其概括為兩部分;用于構(gòu)建服務(wù)器的標(biāo)準(zhǔn)化通信接口,以及用于每個協(xié)議的一組標(biāo)準(zhǔn)消息格式。
然而,它的主要目標(biāo)是提供一種方法來編寫 HTTP/2 和 WebSocket 代碼以及正常的 HTTP 處理代碼;這個設(shè)計的一部分意味著確保有一個簡單的路徑來使用現(xiàn)有的 WSGI 服務(wù)器和應(yīng)用程序,因為絕大多數(shù) Python web 使用依賴于 WSGI,并且提供一個簡單的前進(jìn)路徑對于采用至關(guān)重要。有關(guān)該互操作性的詳細(xì)信息包含在 ASGI-HTTP 規(guī)范中。
2.1.3 概述
ASGI 由兩個不同的組件組成:
- 一個協(xié)議服務(wù)器,它終止套接字并將它們轉(zhuǎn)換為連接和每個連接的事件消息。
- 位于協(xié)議服務(wù)器 內(nèi)的應(yīng)用程序,每個連接調(diào)用一次,并在事件消息發(fā)生時處理它們,并在必要時發(fā)送它自己的事件消息。
與 WSGI 一樣,服務(wù)器在其中托管應(yīng)用程序,并以標(biāo)準(zhǔn)化格式將傳入請求分派給它。然而,與 WSGI 不同,應(yīng)用程序是異步可調(diào)用對象而不是簡單的可調(diào)用對象,它們通過接收和發(fā)送異步事件消息而不是接收單個輸入流并返回單個可迭代對象與服務(wù)器進(jìn)行通信。ASGI 應(yīng)用程序必須作為async/await兼容的協(xié)程運(yùn)行 (即asyncio-compatible)(在主線程上;如果需要同步代碼,它們可以自由使用線程或其他進(jìn)程)。
與 WSGI 不同,ASGI 連接有兩個獨(dú)立的部分:
- 一個***連接范圍***,它代表與用戶的協(xié)議連接,并在連接關(guān)閉之前一直存在。
- 事件,即連接上發(fā)生的事情時發(fā)送到應(yīng)用程序的消息,以及應(yīng)用程序發(fā)回以供服務(wù)器接收的消息,包括要傳輸?shù)娇蛻舳说臄?shù)據(jù)。
應(yīng)用程序通過一個連接scope和兩個可等待的可調(diào)用對象來調(diào)用和等待receive事件消息和send事件消息返回。所有這些都發(fā)生在一個異步事件循環(huán)中。
應(yīng)用程序 callable 的每次調(diào)用都映射到單個傳入的“套接字”或連接,并且如果需要清理,預(yù)計會持續(xù)該連接的生命周期加上更長的時間。某些協(xié)議可能不使用傳統(tǒng)套接字;預(yù)計這些協(xié)議的 ASGI 規(guī)范將定義范圍生命周期是什么以及何時關(guān)閉。
2.2 規(guī)格詳情
2.2.1 連接范圍
用戶與 ASGI 應(yīng)用程序的每個連接都會導(dǎo)致調(diào)用可調(diào)用的應(yīng)用程序來完全處理該連接。這個存在多久,以及描述每個特定連接的信息,稱為 連接范圍。
密切相關(guān)的是,傳遞給可調(diào)用應(yīng)用程序的第一個參數(shù)是一個 scope字典,其中包含描述該特定連接的所有信息。
例如,在 HTTP 下,連接范圍只持續(xù)一個請求,但scope 傳遞的包含大部分請求數(shù)據(jù)(除了 HTTP 請求正文,因為這是通過事件流式傳輸?shù)?#xff09;。
但是,在 WebSocket 下,只要套接字已連接,連接范圍就會持續(xù)。而scope通過包含類似的WebSocket的路徑信息, 但是諸如傳入消息之類的細(xì)節(jié)以事件的形式傳遞 。
某些協(xié)議可能會預(yù)先為您scope提供非常有限的信息,因為它們封裝了諸如握手之類的內(nèi)容。每個協(xié)議定義必須包含有關(guān)其連接范圍持續(xù)多長時間的信息,以及您將在scope參數(shù)中獲得哪些信息。
根據(jù)協(xié)議規(guī)范,應(yīng)用程序在與客戶端通信之前可能必須等待初始打開消息。
2.2.2 事件
ASGI 將協(xié)議分解為應(yīng)用程序必須 接收和響應(yīng)的一系列事件,以及應(yīng)用程序可能發(fā)送的響應(yīng)事件。對于 HTTP,這就像按順序接收兩個事件一樣簡單-http.request 和 http.disconnect ,然后 發(fā)送 回相應(yīng)的事件消息。對于像 WebSocket 這樣的東西,它可能更像是接收 websocket.connect 、 發(fā)送一個 websocket.send、接收一個 websocket.receive、最后 接收一個 websocket.disconnect。
每個事件dict都有一個頂級type鍵,其中包含消息類型的 Unicode 字符串。用戶可以自由創(chuàng)造他們自己的消息類型,并在應(yīng)用程序?qū)嵗g為高級事件發(fā)送它們 - 例如,聊天應(yīng)用程序可能會發(fā)送用戶類型為 mychat.message. 應(yīng)用程序應(yīng)該能夠處理一組混合的事件,一些來自傳入的客戶端連接,一些來自應(yīng)用程序的其他部分。
因為這些消息可以通過網(wǎng)絡(luò)發(fā)送,所以它們需要可序列化,因此它們只允許包含以下類型:
- 字節(jié)串
- Unicode 字符串
- 整數(shù)(在有符號的 64 位范圍內(nèi))
- 浮點數(shù)(在 IEEE 754 雙精度范圍內(nèi);無 Nan或無窮大)
- 列表(元組應(yīng)編碼為列表)
- 字典(鍵必須是 Unicode 字符串)
- 布爾值
- None
2.2.3 應(yīng)用
ASGI 應(yīng)用程序應(yīng)該是單個異步可調(diào)用的:
coroutine application(scope, receive, send)- scope: 連接范圍信息,一個字典,至少包含一個type指定傳入?yún)f(xié)議的 鍵
- receive: 一個可等待的可調(diào)用對象,當(dāng)一個可用的事件字典可用時將產(chǎn)生一個新的事件字典
- send: 一個可等待的可調(diào)用對象,將單個事件字典作為位置參數(shù),一旦發(fā)送完成或連接關(guān)閉,它將返回
每個“連接”都會調(diào)用一次應(yīng)用程序。連接的定義及其壽命由相關(guān)協(xié)議規(guī)范決定。例如,對于 HTTP,它是一個請求,而對于 WebSocket,它是單個 WebSocket 連接。
scope您發(fā)送和接收的事件消息的類型和格式均由應(yīng)用程序協(xié)議之一定義。scope必須是 dict. 密鑰scope["type"]將始終存在,并可用于確定傳入的協(xié)議。密鑰 scope["asgi"]也將作為包含scope["asgi"]["version"]對應(yīng)于服務(wù)器實現(xiàn)的 ASGI 版本的密鑰的字典出現(xiàn) 。如果缺少,版本應(yīng)默認(rèn)為"2.0".
也可能有一個特定于規(guī)范的版本作為 scope["asgi"]["spec_version"]. 這允許單獨(dú)的協(xié)議規(guī)范在不影響整個 ASGI 版本的情況下進(jìn)行增強(qiáng)。
特定于協(xié)議的子規(guī)范涵蓋了這些范圍和事件消息格式。它們等同environ于 WSGI 字典中的鍵規(guī)范。
3. WSGI和ASGI的區(qū)別
WSGI succeeded in allowing much more freedom and innovation in the Python web space, and ASGI’s goal is to continue this onward into the land of asynchronous Python.
You may ask “why not just upgrade WSGI”? This has been asked many times over the years, and the problem usually ends up being that WSGI’s single-callable interface just isn’t suitable for more involved Web protocols like WebSocket.
WSGI applications are a single, synchronous callable that takes a request and returns a response; this doesn’t allow for long-lived connections, like you get with long-poll HTTP or WebSocket connections.
Even if we made this callable asynchronous, it still only has a single path to provide a request, so protocols that have multiple incoming events (like receiving WebSocket frames) can’t trigger this.
WSGI 成功地在 Python 網(wǎng)絡(luò)空間中提供了更多的自由和創(chuàng)新,而 ASGI 的目標(biāo)是將這一點繼續(xù)推進(jìn)到異步 Python 的領(lǐng)域。
你可能會問“為什么不升級 WSGI”?多年來,這個問題已經(jīng)被問過很多次了,問題通常最終是 WSGI 的單一可調(diào)用接口不適合更多涉及的 Web 協(xié)議,如 WebSocket。
WSGI 應(yīng)用程序是一個單一的、同步的可調(diào)用對象,它接受一個請求并返回一個響應(yīng);這不允許長期連接,就像使用長輪詢 HTTP 或 WebSocket 連接一樣。
即使我們使這個可調(diào)用的異步,它仍然只有一個路徑來提供請求,因此具有多個傳入事件(如接收 WebSocket 幀)的協(xié)議無法觸發(fā)它.
總結(jié)
以上是生活随笔為你收集整理的Django3 --- ASGI的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。