Sanic基准测试
uvloop
安装 uvloop
pip install uvloop
import asyncio
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
安装 Sanic
pip install sanic
创建第一个 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)
路由(Routing)
@app.route('/')
def index():
return text('Index Page')
@app.route('/hello')
def hello():
return text('Hello World')
变量规则
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))
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))
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 构建
@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 = 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)
-
把一个应用分解为一套蓝图。这是针对大型应用的理想方案:一个项目可以实例化一个 应用,初始化多个扩展,并注册许多蓝图。
-
在一个应用的 URL 前缀和(或)子域上注册一个蓝图。 URL 前缀和(或)子域的参数 成为蓝图中所有视图的通用视图参数(缺省情况下)。
-
使用不同的 URL 规则在应用中多次注册蓝图。
-
通过蓝图提供模板过滤器、静态文件、模板和其他工具。蓝图不必执行应用或视图 函数。
blueprint 示例
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)
使用蓝图注册全局中间件
@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))
使用蓝图处理静态文件
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))
操作请求数据
为什么不像Flask 一样提供一个全局变量 request?
Flask 是同步请求,每次请求都有一个独立的新线程来处理,这个线程中也只处理这一个请求。而Sanic是基于协程的处理方式,一个线程可以同时处理几个、几十个甚至几百个请求,把request作为全局变量显然会比较难以处理。
json(any) json body
from sanic.response import json
@app.route("/json")
def post_json(request):
return json({ "received": True, "message": request.json })
args(dict) URL请求参数
{'key1': ['value1'], 'key2': ['value2']}
raw_args(dict) 和args 类似
{'key1': 'value1', 'key2': 'value2'}
form(dict)处理 POST 表单请求,数据是一个字典
body(bytes)处理POST 表单请求,数据是一个字符串
-
file
-
ip
-
app
-
url
-
scheme
-
path
-
query_string
关于响应
-
文本
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
)
配置管理
配置入门
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)
使用配置文件
app = Sanic('myapp')
app.config.from_envvar('MYAPP_SETTINGS')
$ export MYAPP_SETTINGS=/path/to/config_file
$ python myapp.py
部署
使用 supervisord 部署
supervisord -c supervisor.conf