[Python]Uvicorn初體驗

vimiix發表於2018-02-26

原文連結:vimiix.com/post/2018/0…

uvicorn簡介

uvicorn是一個基於asyncio開發的一個輕量級高效的web伺服器框架。

官網:www.uvicorn.org

uvicorn 設計的初衷是想要實現兩個目標:

它目前支援httpwebsocketsPub/Sub 廣播,並且可以擴充套件到其他協議和訊息型別。

安裝使用

uvicorn 僅支援python 3.5.3以上版本,我們可以通過pip3來快速的安裝。

Tip:建議和我一樣,直接使用pip3來安裝,就不用關心繫統預設版本了。
➜ pip3 install uvicorn
	...
Successfully installed gunicorn-19.7.1 httptools-0.0.10 
uvicorn-0.0.15 uvloop-0.9.1 websockets-4.0.1
複製程式碼

安裝成功以後。就可以來編寫我們的伺服器應用程式碼了。

先建立一個應用檔案app.py(名字可以自取)

在這個檔案中,來編寫一個簡單的伺服器應用。

1 # coding:utf-8
2
3 async def hello_world(message, channels):
4     content = b'<h1>Hello World</h1>'
5     resp = {
6         'status': 200,
7         'headers': [[b'content-type', b'text/html'],],
8         'content': content,
9     }
10     await channels['reply'].send(resp)
11
複製程式碼

寫好以後,先來嘗試執行一下,跑通了再看程式碼中具體內容的含義。

執行方式是: uvicorn 檔名:callable物件名

➜ uvicorn app:hello_world
複製程式碼

提示下面的內容就表示伺服器啟動成功了

(資訊中包括了訪問地址和埠號,以及worker執行的執行緒id)

[2018-02-26 00:48:52 +0800] [55984] [INFO] Starting gunicorn 19.7.1
[2018-02-26 00:48:52 +0800] [55984] [INFO] Listening at: http://127.0.0.1:8000 (55984)
[2018-02-26 00:48:52 +0800] [55984] [INFO] Using worker: uvicorn 0.0.15
[2018-02-26 00:48:52 +0800] [55987] [INFO] Booting worker with pid: 55987

複製程式碼

這時候我們在瀏覽器中訪問http://127.0.0.1:8000,會看到網頁上顯示出 h1 號字型的Hello World,也就是我們程式碼中定義的 content 的字串內容。

OK,伺服器跑起來了,接下來,我們來看一下程式碼是如何將內容返回給瀏覽器的。

介面分析

在程式碼中我們定義了一個協程函式,ASGI的協議要求應用應該對外暴露一個可接受 messagechannels 這兩個引數的協程可呼叫物件(callable):

  • message:一個ASGI訊息(但有區別,見下文)
  • channels:一個字典( <unicode string> : <channel interface>

<channel interface> 是具有以下屬性的物件:

  • .send(message) 一個協程,用於傳送返回的訊息。可選
  • .receive() 一個協程,用於接收進來的訊息。可選
  • .name 一個unicode字串,channel的唯一標識。可選

uvicorn中的message區別於ASGI中的訊息:

  • 訊息還包括一個channel關鍵字,區別訊息型別,例如:'channel':'http.request'
  • 訊息不包括例如reply_channelbody_channel這樣的channel名稱,,而是channels字典可以檢視允許的channel型別。

舉例:

傳進來的一個HTTP請求可能會是類似下面這樣的messagechannels.

message

{
    'channel': 'http.request',
    'scheme': 'http',
    'root_path': '',
    'server': ('127.0.0.1', 8000),
    'http_version': '1.1',
    'method': 'GET',
    'path': '/',
    'headers': [
        [b'host', b'127.0.0.1:8000'],
        [b'user-agent', b'curl/7.51.0'],
        [b'accept', b'*/*']
    ]
}
複製程式碼

channels

{
    'reply': <ReplyChannel>
}
複製程式碼

為了做出響應,應用程式需要向replychannel傳送(.send())一個http響應,例如:

await channels['reply'].send({
    'status': 200,
    'headers': [
        [b'content-type', b'text/plain'],
    ],
    'content': b'Hello, world'
})
複製程式碼