`

python web 框架 Sanci 快速入门

阅读更多

Sanic 是一个和类Flask 的基于Python3.5+的web框架,它编写的代码速度特别快。除了像Flask 以外,Sanic 还支持以异步请求的方式处理请求。这意味着你可以使用新的 async/await 语法,编写非阻塞的快速的代码。

关于 asyncio 包的介绍,请参考之前的一篇文章 python并发2:使用asyncio处理并发Github 地址 是 https://github.com/channelcat/sanic,感兴趣的可以去贡献代码。

既然它说速度特别快,我们先看下官方提供的 基准测试结果。

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
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一模一样。

路由(Routing)

路由用于把一个函数绑定到一个 URL。下面是一些基本的例子:

@app.route('/')
def index():
return text('Index Page')

@app.route('/hello')
def hello():
return text('Hello World')

当然,你还可以动态的变化URL的某些部分,还可以为一个函数指定多个规则。

变量规则

通过把 URL 的一部分标记为 <variable_name style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important;">就可以在 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))

HTTP 请求类型

默认情况下,我们定义的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))

add_route 方法

除了@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 构建

如果可以匹配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

使用蓝图(Blueprint)

Sanic也提供了和Flask 类似的 Blueprint。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)

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

如果要创建页面链接,可以和通常一样使用 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 对象常用参数有

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 将转变为

{'key1': ['value1'], 'key2': ['value2']}

raw_args(dict) 和args 类似

?key1=value1&key2=value2 将转变为

{'key1': 'value1', 'key2': 'value2'}

form(dict)处理 POST 表单请求,数据是一个字典

body(bytes)处理POST 表单请求,数据是一个字符串

其他参数还有:

  • file

  • ip

  • app

  • url

  • scheme

  • path

  • query_string

详细信息参考文档: Request Data(http://sanic.readthedocs.io/en/latest/sanic/request_data.html)

关于响应

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 部署

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。

分享到:
评论

相关推荐

    Python web框架.Flask中文手册.pdf

    - **微框架概念**:“Micro”在这里指的是Flask作为一款轻量级(或称为微型)Web框架的概念。 - **设计哲学**:Flask的设计理念是“轻巧”,它没有内置数据库抽象层、表单验证等功能,这些功能可以通过扩展来实现。 ...

    Python+Web

    Django是一个高级的Python Web框架,鼓励快速开发和干净、实用的设计。Flask是一个用Python编写的轻量级Web应用框架,它也遵循了"约定优于配置"的原则,但比Django更加灵活。 讲义中提到的洪强宁是一位经验丰富的...

    《Python快速编程入门》-课后题答案.pdf

    1. **Web应用开发**:Python中的Django和Flask等框架使得构建Web应用变得高效快捷。 2. **操作系统管理与服务器运维自动化**:通过模块如os、sys、subprocess等,可以方便地进行系统管理和自动化任务。 3. **科学...

    python快速入门

    ### Python快速入门知识点详解 #### 一、Python简介与特色 - **简介**:Python是一种高级编程语言,因其简洁易读的语法而受到广泛欢迎。无论是编程新手还是经验丰富的开发者,都能快速上手并利用Python解决复杂的...

    Python快速编程入门习题参考答案.docx

    例如,Web开发中常用的框架有Django和Flask;科学计算领域有NumPy、Pandas和SciPy等库;在游戏开发中,虽然Python不是首选,但也有Pygame等库支持。 Python的特点包括但不限于: 1. 简洁明了的语法,易于学习和理解...

    python语言从入门到精通

    1. **Web开发**:通过Django或Flask框架学习构建Web应用,包括路由、模板、数据库交互等。 2. **数据分析**:使用Pandas库进行数据清洗、预处理,Numpy进行数值计算,Matplotlib和Seaborn进行数据可视化。 3. **...

    Python技术快速入门指南.docx

    ### Python技术快速入门指南 #### 一、Python的基本概念与特点 Python 是一门现代的、通用的编程语言,以其简洁的语法和强大的功能受到广大程序员的喜爱。作为一种解释型语言,Python 不需要编译就能直接运行,这...

    Python基础入门资料

    ### Python基础入门资料 #### Python语言介绍 **1.1 Python起源** Python 由荷兰程序员吉多·范罗苏姆(Guido van Rossum)在1989年圣诞节期间开始设计开发。他最初的想法是创建一个新的脚本解释程序,以此来继承...

    Python编程:从入门到实践-第6次1

    最后,书中的第三个项目是创建和定制一个简单的Web应用,可能涵盖基础的Web框架如Flask或Django,让读者体验Web开发的基本流程。 这本书适合对Python感兴趣的所有层次的读者,无论是初学者还是有一定经验的开发者,...

    python 地址.docx

    Python 的设计哲学强调代码的可读性和简洁的语法,同时拥有丰富的标准库和第三方库支持,使得 Python 在多个领域都有广泛的应用,如 Web 开发、数据分析、人工智能、网络爬虫等。 ### Python 版本介绍 Python 目前...

    Python技术入门指南.docx

    Web开发方面,Django和Flask框架简化了Web应用的构建,BeautifulSoup和Scrapy等库则助力Web爬虫的编写。此外,Python在自动化脚本编写中表现出色,能提高工作效率。 为了进一步学习Python,除了阅读本文档,你还应...

    Python从入门到实践第三版源码+练习+PPT

    Python从入门到实践第三版源码+练习+PPT是一套全面覆盖Python编程基础知识的资源包。它不仅提供了Python的实战源码,还包括了配套的练习题以及教学PPT,非常适合初学者构建扎实的编程基础。 首先,资源包中的源码...

    Python毕业生信息审核系统源码

    Python毕业生信息审核...总之,Python毕业生信息审核系统源码是一个集成了Python编程、Web框架、数据库管理、用户界面设计、工作流管理、安全性和测试等多方面知识的项目,对于学习和提升Python Web开发技能大有裨益。

    python入门到高级全栈工程师培训 第3期 附课件代码

    python入门到高级全栈工程师培训视频学习资料;本资料仅用于学习,请查看后24小时之内删除。 【课程内容】 第1章 01 计算机发展史 02 计算机系统 03 小结 04 数据的概念 05 进制转换 06 原码补码反码 07 物理层和...

    精品课件 Python从入门到精通 第2章 Python语言基础(共32页).ppt

    这只是Python语言基础的冰山一角,后续章节将涵盖运算符与表达式、流程控制语句、列表与元组、字典与集合、字符串处理、正则表达式、函数、面向对象编程、模块、异常处理、文件操作、数据库操作、GUI编程、网络爬虫...

    python简明教程中文,python简明教程中文pdf,Python

    3. **Web开发**:Django和Flask是流行的Python Web框架,用于构建高效且灵活的Web应用。 4. **自动化脚本**:Python可以用于系统管理任务,如文件操作、定时任务等。 5. **人工智能**:利用TensorFlow、Keras或...

    Python入门介绍

    Django和Flask等Web框架则在互联网应用开发中扮演着重要角色;TensorFlow、PyTorch等深度学习库也被广大数据科学家和机器学习工程师所青睐。 在Python中,开发者可以编写非常简洁的代码来实现复杂的逻辑。例如,在...

    Python Flask入门与进阶 开发电影网站视频课程

    Flask 是一个基于 Python 的轻量级 Web 应用框架,它以简洁著称,非常适合初学者快速上手并进行实际项目开发。Flask 本身并不包含太多额外的功能,这种设计使得开发者可以根据自己的需求自由选择第三方库来增强应用...

    python-3.7.3.rar

    Python 3.7.3是3.7系列的第三次重大维护更新,旨在提供更好的稳定性和错误修复。 2. **64位支持**:Python 3.7.3的64位版本允许程序处理超过4GB的数据,这对于大数据分析、机器学习和高性能计算等场景至关重要。在...

Global site tag (gtag.js) - Google Analytics