python 最快 web 框架 Sanic 快速入門
來源:goodspeed
簡介
Sanic 是一個和類Flask 的基於Python3.5+的web框架,它編寫的代碼速度特別快。
除了像Flask 以外,Sanic 還支持以非同步請求的方式處理請求。這意味著你可以使用新的 async/await 語法,編寫非阻塞的快速的代碼。
既然它說速度特別快,我們先看下官方提供的 基準測試結果。
Sanic基準測試
這個測試的程序運行在 AWS 實例上,系統是Ubuntu,只使用了一個進程。
Sanic 的開發者說他們的靈感來自於這篇文章 uvloop: Blazing fast Python networking。
那我們就有必要看下uvloop是個什麼庫。
uvloop
uvloop 是 asyncio 默認事件循環的替代品,實現的功能完整,切即插即用。uvloop是用CPython 寫的,建於libuv之上。
uvloop 可以使 asyncio 更快。事實上,它至少比 nodejs、gevent 和其他 Python 非同步框架要快兩倍 。基於 uvloop 的 asyncio 的速度幾乎接近了 Go 程序的速度。
安裝 uvloop
uvloop 還只能在 *nix 平台 和 Python3.5+以上版本使用。
使用pip安裝:
pip install uvloop
在 asyncio 代碼中使用uvloop 也很簡單:
import asyncio
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
這得代碼使得對任何asyncio.get_event_loop() 的調用都將返回一個uvloop實例。
詳細的uvloop 介紹可以看下原文:uvloop: Blazing fast Python networking。
uvloop的github地址是https://github.com/MagicStack/uvloop。
現在我們開始學習Sanic:
安裝 Sanic
pip install sanic
創建第一個 sanic 代碼
from sanic import Sanic
fromsanic.response import text
app=Sanic(__name__)
@app.route("/")
async def test(request):
returntext( Hello world! )
app.run(host="0.0.0.0",port=8000,debug=True)
運行代碼: python main.py, 現在打開瀏覽器訪問 http://0.0.0.0:8000,你會看到 hello world!。
如果你熟悉Flask,你會發現,這個語法簡直和Flask一模一樣。
路由(Routing)
路由用於把一個函數綁定到一個 URL。下面是一些基本的例子:
@app.route( / )
def index():
returntext( Index Page )
@app.route( /hello )
def hello():
returntext( Hello World )
當然,你還可以動態的變化URL的某些部分,還可以為一個函數指定多個規則。
變數規則
通過把 URL 的一部分標記為就可以在 URL 中添加變數。標記的 部分會作為關鍵字參數傳遞給函數。通過使用,可以 選擇性的加上一個轉換器,為變數指定特定的類型,如果傳入的類型錯誤,Sanic會拋出NotFound異常。請看下面的例子:
fromsanic.response importtext
@app.route( /tag/ )
async def tag_handler(request,tag):
returntext( Tag - {} .format(tag))
@app.route( /number/ )
async def integer_handler(request,integer_arg):
returntext( Integer - {} .format(integer_arg))
@app.route( /number/ )
async def number_handler(request,number_arg):
returntext( Number - {} .format(number_arg))
@app.route( /person/ )
async def person_handler(request,name):
returntext( Person - {} .format(name))
@app.route( /folder/ )
async def folder_handler(request,folder_id):
returntext( Folder - {} .format(folder_id))
HTTP 請求類型
默認情況下,我們定義的URL只支持GET 請求,@app.route裝飾器提供了一個可選參數methods,這個參數允許傳入所有HTTP 方法。
例如:
fromsanic.response importtext
@app.route( /post ,methods=[ POST ])
async def post_handler(request):
returntext( POST request - {} .format(request.json))
@app.route( /get ,methods=[ GET ])
async def get_handler(request):
returntext( GET request - {} .format(request.args))
也可以簡寫為:
fromsanic.response importtext
@app.post( /post )
async def post_handler(request):
returntext( POST request - {} .format(request.json))
@app.get( /get )
async def get_handler(request):
returntext( GET request - {} .format(request.args))
add_route 方法
除了@app.route裝飾器,Sanic 還提供了 add_route 方法。
@app.route 只是包裝了 add_route方法。
fromsanic.response importtext
# Define the handler functions
async def handler1(request):
returntext( OK )
async def handler2(request,name):
returntext( Folder - {} .format(name))
async def person_handler2(request,name):
returntext( Person - {} .format(name))
# Add each handler function as a route
app.add_route(handler1, /test )
app.add_route(handler2, /folder/ )
app.add_route(person_handler2, /person/ ,methods=[ GET ])
URL 構建
如果可以匹配URL,那麼Sanic可以生成URL嗎?當然可以,url_for() 函數就是用於構建指定函數的URL的。它把函數名稱作為第一個參數,其餘參數對應URL中的變數,例如:
@app.route( / )
async def index(request):
# generate a URL for the endpoint `post_handler`
url=app.url_for( post_handler ,post_id=5)
# the URL is `/posts/5`, redirect to it
returnredirect(url)
@app.route( /posts/
)
async def post_handler(request,post_id):
returntext( Post - {} .format(post_id))
未定義變數會作為URL的查詢參數:
url=app.url_for( post_handler ,post_id=5,arg_one= one ,arg_two= two )
# /posts/5?arg_one=one&arg_two=two
# 支持多值參數
url=app.url_for( post_handler ,post_id=5,arg_one=[ one , two ])
# /posts/5?arg_one=one&arg_one=two
使用藍圖(Blueprint)
Sanic也提供了和Flask 類似的 Blueprint。
Blueprint有以下用途:
把一個應用分解為一套藍圖。這是針對大型應用的理想方案:一個項目可以實例化一個 應用,初始化多個擴展,並註冊許多藍圖。
在一個應用的 URL 前綴和(或)子域上註冊一個藍圖。 URL 前綴和(或)子域的參數 成為藍圖中所有視圖的通用視圖參數(預設情況下)。
使用不同的 URL 規則在應用中多次註冊藍圖。
通過藍圖提供模板過濾器、靜態文件、模板和其他工具。藍圖不必執行應用或視圖 函數。
blueprint 示例
from sanic import Sanic
fromsanic.response import json
from sanic import Blueprint
bp=Blueprint( my_blueprint )
@bp.route( / )
async def bp_root(request):
returnjson({ my : blueprint })
app=Sanic(__name__)
app.blueprint(bp)
app.run(host= 0.0.0.0 ,port=8000,debug=True)
Sanic 使用 app.blueprint() 方法註冊blueprint。
使用藍圖註冊全局中間件
@bp.middleware
async def print_on_request(request):
print("I am a spy")
@bp.middleware( request )
async def halt_request(request):
returntext( I halted the request )
@bp.middleware( response )
async def halt_response(request,response):
returntext( I halted the response )
使用藍圖處理異常
@bp.exception(NotFound)
def ignore_404s(request,exception):
returntext("Yep, I totally found the page: {}".format(request.url))
使用藍圖處理靜態文件
第一個參數指向當前的Python包
第二個參數是靜態文件的目錄
bp.static( /folder/to/serve , /web/path )
使用url_for
如果要創建頁面鏈接,可以和通常一樣使用 url_for() 函數,只是要把藍圖名稱作為端點的前綴,並且用一個點( . )來 分隔:
@blueprint_v1.route( / )
async def root(request):
url=app.url_for( v1.post_handler ,post_id=5)# --> /v1/post/5
returnredirect(url)
@blueprint_v1.route( /post/
)
async def post_handler(request,post_id):
returntext( Post {} in Blueprint V1 .format(post_id))
操作請求數據
對於web 應用來說對客戶端向伺服器發送的數據做出相應很重要,在Sanic中由傳入的參數 request來提供請求信息。
為什麼不像Flask 一樣提供一個全局變數 request?
Flask 是同步請求,每次請求都有一個獨立的新線程來處理,這個線程中也只處理這一個請求。而Sanic是基於協程的處理方式,一個線程可以同時處理幾個、幾十個甚至幾百個請求,把request作為全局變數顯然會比較難以處理。
Request 對象常用參數有
json(any) json body
fromsanic.response importjson
@app.route("/json")
def post_json(request):
returnjson({"received":True,"message":request.json})
args(dict) URL請求參數
raw_args(dict) 和args 類似
form(dict)處理 POST 表單請求,數據是一個字典
body(bytes)處理POST 表單請求,數據是一個字元串
其他參數還有:
file
ip
app
url
scheme
path
query_string
詳細信息參考文檔: Request Data
關於響應
Sanic使用response 函數創建響應對象。
文本 response.text( hello world )
html response.html(
hello world
)
json response.json({ hello : world })
file response.file( /srv/www/hello.txt )
streaming
from sanic importresponse
@app.route("/streaming")
async def index(request):
async def streaming_fn(response):
response.write( foo )
response.write( bar )
returnresponse.stream(streaming_fn,content_type= text/plain )
redirect response.file( /json )
raw response.raw( raw data )
如果想修改響應的headers可以傳入headers 參數
from sanic importresponse
@app.route( /json )
def handle_request(request):
returnresponse.json(
{ message : Hello world! },
headers={ X-Served-By : sanic },
status=200
)
配置管理
應用總是需要一定的配置的。根據應用環境不同,會需要不同的配置。比如開關調試 模式、設置密鑰以及其他依賴於環境的東西。
Sanic 的設計思路是在應用開始時載入配置。你可以在代碼中直接硬編碼寫入配置,也可以使用配置文件。
不管你使用何種方式載入配置,都可以使用 Sanic 的 config 屬性來操作配置的值。 Sanic 本身就使用這個對象來保存 一些配置,擴展也可以使用這個對象保存配置。同時這也是你保存配置的地方。
配置入門
config 實質上是一個字典的子類,可以像字典一樣操作:
app=Sanic( myapp )
app.config.DB_NAME= appdb
app.config.DB_USER= appuser
也可以一次更新多個配置:
db_settings={
DB_HOST : localhost ,
DB_NAME : appdb ,
DB_USER : appuser
}
app.config.update(db_settings)
從對象導入配置
importmyapp.default_settings
app=Sanic( myapp )
app.config.from_object(myapp.default_settings)
這裡是我寫的聊天機器人的真實配置示例:https://github.com/gusibi/momo/
使用配置文件
如果把配置放在一個單獨的文件中會更有用。理想情況下配置文件應當放在應用包的 外面。這樣可以在修改配置文件時不影響應用的打包與分發
常見用法如下:
app=Sanic( myapp )
app.config.from_envvar( MYAPP_SETTINGS )
首先從 myapp.default_settings 模塊載入配置,然後根據 MYAPP_SETTINGS 環境變數所指向的文件的內容重載配置的值。在 啟動伺服器前,在 Linux 或 OS X 操作系統中,這個環境變數可以在終端中使用 export 命令來設置:
$exportMYAPP_SETTINGS=/path/to/config_file
$pythonmyapp.py
部署
Sanic 項目還不是特別成熟,現在部署比較簡陋。對Gunicorn的支持也不完善。
詳細信息可以 看下這個問題 Projects built with sanic?
先在說下我的部署方式
使用 supervisord 部署
supervisord 配置文件: https://github.com/gusibi/momo/blob/master/supervisord.conf
啟動 方式
supervisord -c supervisor.conf
總結
試用了下Sanic,把之前的一個聊天機器人從Flask 改成了 Sanic。不得不說,如果你有Flask經驗,大致看一下Sanic文檔就可以直接上手了。
並且Sanic 的速度比Flask 快很多,只是Sanic配套的包還是太少,用於生產環境有一定的風險。
參考鏈接
uvloop: Blazing fast Python networking
Sanic Githu 地址
Sanic 文檔
看完本文有收穫?請轉發分享給更多人
關注「Python開發者」,提升Python技能


※金融數據分析全新升級 | 學好風控,日進斗金
※現代情感分析方法
※共享經濟+大數據?這或許是一個值得考慮的模式
※如何在3個月內成為數據科學家,挑戰起薪30萬
※我是這樣挑戰不用 for 循環的
TAG:Python開發者 |
※Python的最快的Web框架快速入門
※Github上最受歡迎的Python框架Flask入門
※spark入門框架+python
※Python 繪圖庫 Matplotlib 入門教程
※Python模塊——contextlib和urllib
※Python 模塊 urllib.parse
※python3+selenium+chrome實戰速成
※GitHub 熱門項目:Python Fire
※Python爬蟲框架之pyspider
※windows下安裝python及第三方庫numpy、scipy、matplotlib終極版
※Python爬蟲學習Scrapy之Spiders
※Python 機器學習 Scikit-learn 完全入門指南
※python selenium環境搭建
※Python中使用Type hinting 和 annotations
※Python 特殊函數(lambda,map,filter,reduce)
※基於 Python Schema 的機器學習庫——Smart Fruit
※Pelican 入門:一個 Python 靜態網站生成器
※python web開發-flask連接sqlite資料庫
※Logistic regression 的梯度下降解法-附python實現
※Python 標準庫精華: collections.Counter