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