Sanic 是一個和類Flask 的基於Python3.5+的web框架,它編寫的代碼速度特別快。
除了像Flask 之外,Sanic 還支持以異步請求的方式處理請求。這意味着你可使用新的 async/await 語法,編寫非阻塞的快速的代碼。html
關於 asyncio 包的介紹,請參考以前的一篇文章 python併發2:使用asyncio處理併發node
Github 地址 是 https://github.com/channelcat/sanic,感興趣的能夠去貢獻代碼。python
既然它說速度特別快,咱們先看下官方提供的 基準測試結果。git
這個測試的程序運行在 AWS 實例上,系統是Ubuntu,只使用了一個進程。github
Sanic 的開發者說他們的靈感來自於這篇文章 uvloop: Blazing fast Python networking。web
那咱們就有必要看下uvloop是個什麼庫。json
uvloop 是 asyncio 默認事件循環的替代品,實現的功能完整,切即插即用。uvloop是用CPython 寫的,建於libuv之上。
uvloop 可使 asyncio 更快。事實上,它至少比 nodejs、gevent 和其餘 Python 異步框架要快兩倍 。基於 uvloop 的 asyncio 的速度幾乎接近了 Go 程序的速度。segmentfault
uvloop 還只能在 *nix 平臺 和 Python3.5+以上版本使用。
使用pip安裝:瀏覽器
pip install uvloop
在 asyncio 代碼中使用uvloop 也很簡單:bash
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:
pip install sanic
from sanic import Sanic from sanic.response import text app = Sanic(__name__) @app.route("/") async def test(request): return text('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如出一轍。
路由用於把一個函數綁定到一個 URL。下面是一些基本的例子:
@app.route('/') def index(): return text('Index Page') @app.route('/hello') def hello(): return text('Hello World')
固然,你還能夠動態的變化URL的某些部分,還能夠爲一個函數指定多個規則。
經過把 URL 的一部分標記爲 <variable_name> 就能夠在 URL 中添加變量。標記的 部分會做爲關鍵字參數傳遞給函數。經過使用 <converter:variable_name>
,能夠 選擇性的加上一個轉換器,爲變量指定特定的類型,若是傳入的類型錯誤,Sanic會拋出NotFound
異常。請看下面的例子:
from sanic.response import text @app.route('/tag/<tag>') async def tag_handler(request, tag): return text('Tag - {}'.format(tag)) @app.route('/number/<integer_arg:int>') async def integer_handler(request, integer_arg): return text('Integer - {}'.format(integer_arg)) @app.route('/number/<number_arg:number>') async def number_handler(request, number_arg): return text('Number - {}'.format(number_arg)) @app.route('/person/<name:[A-z]>') async def person_handler(request, name): return text('Person - {}'.format(name)) @app.route('/folder/<folder_id:[A-z0-9]{0,4}>') async def folder_handler(request, folder_id): return text('Folder - {}'.format(folder_id))
默認狀況下,咱們定義的URL只支持GET
請求,@app.route
裝飾器提供了一個可選參數methods
,這個參數容許傳入全部HTTP 方法。
例如:
from sanic.response import text @app.route('/post', methods=['POST']) async def post_handler(request): return text('POST request - {}'.format(request.json)) @app.route('/get', methods=['GET']) async def get_handler(request): return text('GET request - {}'.format(request.args))
也能夠簡寫爲:
from sanic.response import text @app.post('/post') async def post_handler(request): return text('POST request - {}'.format(request.json)) @app.get('/get') async def get_handler(request): return text('GET request - {}'.format(request.args))
除了@app.route
裝飾器,Sanic 還提供了 add_route
方法。
@app.route
只是包裝了add_route
方法。
from sanic.response import text # Define the handler functions async def handler1(request): return text('OK') async def handler2(request, name): return text('Folder - {}'.format(name)) async def person_handler2(request, name): return text('Person - {}'.format(name)) # Add each handler function as a route app.add_route(handler1, '/test') app.add_route(handler2, '/folder/<name>') app.add_route(person_handler2, '/person/<name:[A-z]>', methods=['GET'])
若是能夠匹配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 return redirect(url) @app.route('/posts/<post_id>') async def post_handler(request, post_id): return text('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
Sanic也提供了和Flask 相似的 Blueprint。
Blueprint有如下用途:
把一個應用分解爲一套藍圖。這是針對大型應用的理想方案:一個項目能夠實例化一個 應用,初始化多個擴展,並註冊許多藍圖。
在一個應用的 URL 前綴和(或)子域上註冊一個藍圖。 URL 前綴和(或)子域的參數 成爲藍圖中全部視圖的通用視圖參數(缺省狀況下)。
使用不一樣的 URL 規則在應用中屢次註冊藍圖。
經過藍圖提供模板過濾器、靜態文件、模板和其餘工具。藍圖沒必要執行應用或視圖 函數。
from sanic import Sanic from sanic.response import json from sanic import Blueprint bp = Blueprint('my_blueprint') @bp.route('/') async def bp_root(request): return json({'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): return text('I halted the request') @bp.middleware('response') async def halt_response(request, response): return text('I halted the response')
@bp.exception(NotFound) def ignore_404s(request, exception): return text("Yep, I totally found the page: {}".format(request.url))
第一個參數指向當前的Python包
第二個參數是靜態文件的目錄
bp.static('/folder/to/serve', '/web/path')
若是要建立頁面連接,能夠和一般同樣使用 url_for() 函數,只是要把藍圖名稱做爲端點的前綴,而且用一個點( . )來 分隔:
@blueprint_v1.route('/') async def root(request): url = app.url_for('v1.post_handler', post_id=5) # --> '/v1/post/5' return redirect(url) @blueprint_v1.route('/post/<post_id>') async def post_handler(request, post_id): return text('Post {} in Blueprint V1'.format(post_id))
對於web 應用來講對客戶端向服務器發送的數據作出相應很重要,在Sanic中由傳入的參數 request來提供請求信息。
爲何不像Flask 同樣提供一個全局變量 request?
Flask 是同步請求,每次請求都有一個獨立的新線程來處理,這個線程中也只處理這一個請求。而Sanic是基於協程的處理方式,一個線程能夠同時處理幾個、幾十個甚至幾百個請求,把request做爲全局變量顯然會比較難以處理。
Request 對象經常使用參數有
from sanic.response import json @app.route("/json") def post_json(request): return json({ "received": True, "message": request.json })
?key1=value1&key2=value2 將轉變爲
{'key1': ['value1'], 'key2': ['value2']}
?key1=value1&key2=value2 將轉變爲
{'key1': 'value1', 'key2': 'value2'}
其餘參數還有:
file
ip
app
url
scheme
path
query_string
詳細信息參考文檔: Request Data
Sanic使用response 函數建立響應對象。
文本 response.text('hello world')
html response.html('<p>hello world</p>')
json response.json({'hello': 'world'})
file response.file('/srv/www/hello.txt')
streaming
from sanic import response @app.route("/streaming") async def index(request): async def streaming_fn(response): response.write('foo') response.write('bar') return response.stream(streaming_fn, content_type='text/plain')
redirect response.file('/json')
raw response.raw('raw data')
若是想修改響應的headers能夠傳入headers 參數
from sanic import response @app.route('/json') def handle_request(request): return response.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)
import myapp.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 命令來設置:
$ export MYAPP_SETTINGS=/path/to/config_file $ python myapp.py
Sanic 項目還不是特別成熟,如今部署比較簡陋。對Gunicorn的支持也不完善。
詳細信息能夠 看下這個問題 Projects built with sanic?
先在說下個人部署方式
supervisord 配置文件: https://github.com/gusibi/momo/blob/master/supervisord.conf
啓動 方式
supervisord -c supervisor.conf
試用了下Sanic,把以前的一個聊天機器人從Flask 改爲了 Sanic。不得不說,若是你有Flask經驗,大體看一下Sanic文檔就能夠直接上手了。
而且Sanic 的速度比Flask 快不少,只是Sanic配套的包仍是太少,用於生產環境有必定的風險。
最後對聊天微信聊天機器人感興趣的能夠看下https://github.com/gusibi/momo。
下一篇將介紹如何使用 Sanic 一步一步建立一個 聊天機器人。
最後,感謝女友支持。
>歡迎關注 | >請我喝芬達 |
---|---|
魔魔是咱們家巴哥的名字
貼一張魔魔的照片結束本篇文章。