官方参考文档:http://sanic.readthedocs.io/en/latest/
Sanic 是一个和类Flask 的基于Python3.5+的web框架,它编写的代码速度特别快。
除了像Flask 以外,Sanic 还支持以异步请求的方式处理请求。这意味着你可以使用新的 async/await 语法,编写非阻塞的快速的代码
既然它说速度特别快,我们先看下官方提供的 基准测试结果。
uvloop: Blazing fast Python networking
现在我们开始学习Sanic:
pip install sanic
Sanic最低支持Python 3.5,如果需要学习Sanic,请先下载版本不低于3.5的Python包。注意:Sanic暂时只能在mac os系统和linux系统下安装,windows系统暂不支持
创建第一个 sanic 代码
from sanic import Sanic
from sanic.response import text
from sanic.response import json
app = Sanic(__name__)
@app.route("/hello")
async def test(request):
return text('Hello world!')
@app.route("/")
async def hello_sanic(request):
data = json({"code":0})
return data
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000, debug=True)
运行代码: python main.py
, 现在打开浏览器访问 http://0.0.0.0:8000/hello,你会看到hello world!
。
用惯Flask的同学,会发现Sanic的用法与Flask有点类似,但是,值得注意的是,当我们写路由的时候,方法中必须带有1个参数,并且Response不能像Flask中是一个str即可,需要将其转化为sanic.response.HTTPResponse
类型。
路由用于把一个函数绑定到一个 URL。下面是一些基本的例子:
@app.route('/')
def index():
return text('Index Page')
@app.route('/hello')
def hello():
return text('Hello World')
当然,你还可以动态的变化URL的某些部分,还可以为一个函数指定多个规则。
通过把 URL 的一部分标记为
就可以在 URL 中添加变量。标记的 部分会作为关键字参数传递给函数。通过使用
,可以 选择性的加上一个转换器,为变量指定特定的类型,如果传入的类型错误,Sanic会抛出NotFound
异常。请看下面的例子:
from sanic.response import text
@app.route('/tag/')
async def tag_handler(request, tag):
return text('Tag - {}'.format(tag))
@app.route('/number/')
async def integer_handler(request, integer_arg):
return text('Integer - {}'.format(integer_arg))
@app.route('/number/')
async def number_handler(request, number_arg):
return text('Number - {}'.format(number_arg))
@app.route('/person/')
async def person_handler(request, name):
return text('Person - {}'.format(name))
@app.route('/folder/')
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/')
app.add_route(person_handler2, '/person/', 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/')
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有以下用途:
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)
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/')
async def post_handler(request, post_id):
return text('Post {} in Blueprint V1'.format(post_id))
对于web 应用来说对客户端向服务器发送的数据做出相应很重要,在Sanic中由传入的参数 request来提供请求信息。
Flask 是同步请求,每次请求都有一个独立的新线程来处理,这个线程中也只处理这一个请求。而Sanic是基于协程的处理方式,一个线程可以同时处理几个、几十个甚至几百个请求,把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'}
form(dict)处理 POST 表单请求,数据是一个字典
body(bytes)处理POST 表单请求,数据是一个字符串
其他参数还有:
详细信息参考文档 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 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'
也可以一次更新多个配置:
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的支持也不完善。
详细信息可以 看下这个问题
链接
supervisord 配置文件
https://github.com/gusibi/momo/blob/master/supervisord.conf
启动 方式
supervisord -c supervisor.conf
Sanic 的速度比Flask 快很多,只是Sanic拓展包还是太少,用于生产环境有一定的风险。
来源:https://www.jianshu.com/p/636833c71c2a
https://www.jb51.net/article/143843.htm