一、前言
为什么要用Sanic,请见 让你的Python(Web应用)飞起来,(异步/协程)全家桶
废话不多讲,直接进入正题,我会按照正常的Web应用程序架构来讲Sanic的用法
二、进入正题
基本操作
- 安装Sanic
pip install sanic
- 导入Sanic
from sanic import Sanic
app = Sanic(__name__)
复制代码
中间件(Middleware And Listeners)
服务启动之前
@app.listener('before_server_start')
async def start_connection(app, loop):
# 执行一些初始化任务
# 例如初始化 Redis 连接池
await RedisPool.init_redis_conn_pool(loop)
复制代码
服务停止之前
@app.listener('after_server_stop')
async def start_connection(app, loop):
# 执行一些备份、应急、关闭任务
# 例如关闭连接池
await RedisPool.close_redis_conn_pool()
await close_connection(app, loop)
复制代码
请求(Request)中间件
@app.middleware('request')
async def add_tokrn_to_request(request):
# 每次请求会经过此方法,可以做一些对request的处理
# 例如添加token,secret,token
await session_interface.open(request)
复制代码
响应(Response)中间件
@app.middleware('response')
async def save_session(request, response):
logger = logging.getLogger('response')
# 每次服务器响应会经过此方法,可以做一些异常,cookie,logger等的处理
# 例如写入cookie,记录用户日志
await session_interface.save(request, response)
复制代码
蓝图(Blueprint)
类似Flask,用来组织应用程序,也就是大多数我们mvc模式中的controller,
- 首先声明一个BP
order_bp = Blueprint('orders', url_prefix='v1/api/order')
# url_prefix:定义路由修正,
# 例如当前蓝图下你定义了一个 query/id 的路由,那么你应该这样访问它 v1/api/order/query/id
复制代码
- 在主程序中(main.py)引用
from controller.order_controller import order_bp
app.blueprint(order_bp)
# 这样order_bp就被注册到sanic中,可以通过浏览器 http://example.com/v1/api/order/query/id 访问
复制代码
- 使用蓝图处理异常
@bp.exception(NotFound)
def ignore_404(request, exception):
return text("Yep, I totally found the page: {}".format(request.url))
复制代码
路由(Routing)
# GET 路由参数的方式
@order_bp.route('/delete/<oid:int>', methods=['GET'])
async def delete_order(request, oid):
result = await OrderModel().del_order_by_id(oid)
if result:
return json({'status': 'ok', 'result': result})
return json({'status': 'fail', 'errmsg': '获取数据失败'})
复制代码
# Get params的方式
@service_bp.route('/message/send', methods=['GET'])
async def send_message(request):
uuid = request.args['uuid'][0]
page = request.args['page'][0]
result = await MessageService().send_message_2_user(uuid,page)
if result:
return json({'status': 'ok', 'result': result})
return json({'status': 'fail', 'errmsg': '获取数据失败'})
return raw(result)
复制代码
# 修改价格
@order_bp.route('/price/modify', methods=['POST'])
async def price_modify(request):
order_id = request.json['order_id']
price = request.json['price']
postage = request.json['postage']
show_price = request.json['show_price']
is_shipping = request.json['is_shipping']
result = await OrderModel().modify_price(order_id, price, postage, show_price, is_shipping)
if result:
return json({'status': 'ok', 'result': result})
return json({'status': 'fail', 'errmsg': '获取数据失败'})
复制代码
响应类型
from sanic.response import json
# text
response.text('hello world')
# html
response.html('<p>hello world</p>')
# json
response.json({'hello': 'world'})
# file
response.file('/srv/www/hello.txt')
# stream
response.stream(stream_ctx, content_type='text/plain')
复制代码
如果我想在Sanic启动之后额外运行任务怎么办?
app.add_task(notify_server_started())
app.add_task(notify_html_spider())
复制代码
三、启动Sanic服务
if __name__ == "__main__":
app.add_task(notify_server_started())
app.add_task(notify_html_spider())
app.run(host="0.0.0.0", port=settings.PORT, workers=settings.workers, debug=settings.DEBUG, access_log=True)
复制代码
在启动时可以传入一些参数,其他的都是字面意思,很好理解,这里讲一下 Workers
workers
接受integer
类型,默认为1,传入4意味着Sanic会为你复制四份,创建四个进程来运行你的Sanic App
如图
多进程状态下,路由会进行自动分配,从而增加吞吐量,
workers
的个数依据自身服务器性能而定,如果创建太多会出现占用情况,同样多进程模式下,你的数据库连接或者数据库连接池的数量也要适当调大,否则会出现连接数过多而拒绝连接的情况
四、获奖感言
Sanic的介绍就这么多,只是一个基本入门,至于其他类似订阅发布监听等等功能大家可以移步 Sanic官网 进行学习和查阅
下期我可能会联合Sanic讲解关于异步Redis(aioredis)的使用,例子打算用类似订单倒计时的功能,结合redis的订阅发布通知来讲