一、背景
最近使用sanic框架开发一个Python后台程序,涉及多个接口开发,且有的接口需要运行耗时较长的任务,不得不使用异步。
看了很多关于异步编程的博客,比如廖雪峰大哥的一篇Python 异步编程入门,亲自实践发现,不是随便加个await/async关键字就可以实现异步,支持异步的库本来就不多,如果业务复杂基本找不到合适的库支持,还好找了好久,终于有了解决办法。
二、如何实现异步sanic接口
下面使用一个小型的sanic框架搭建的服务来说明
1、代码目录结构及说明
- |— bp.py 接口蓝本,定义了一个可供外部调用的GET接口
- |— interface.py 接口具体实现
- |— app.py 服务启动入口
2、bp.py
通过blueprint定义接口,调用后及时返回响应信息,异步后台运行InterfaceImpl.df方法,无需等待。
# !/usr/bin/env python3
# -*- coding : utf-8 -*-
# @Time : 2020/11/9 16:27
# @Author : Attaboy
# @Email : slum2183@163.com
# @File : bp.py
# @Software : PyCharm
from sanic import response
from sanic import Blueprint
from interface import InterfaceImpl
# strict_slashes 控制请求的URL严格匹配
test_bp = Blueprint('test_bp', strict_slashes=True)
# 定义接口
@test_bp.route('/df', methods=['GET', 'OPTIONS'])
async def df(request):
await InterfaceImpl.df(request)
return response.json({"msg": "cmd running success"})
3、interface.py
上面定义的接口具体实现,先获取事件循环,再让封装好的函数os.system运行在指定的线程里,这里使用ping程序模拟耗时任务,后面有测试结果。
# !/usr/bin/env python3
# -*- coding : utf-8 -*-
# @Time : 2021/1/29 16:57
# @Author : Attaboy
# @Email : slum2183@163.com
# @File : interface.py
# @Software : PyCharm
import asyncio
import os
import uvloop
# uvloop是asyncio中的事件循环的替代方案,替换后可以使得asyncio性能提高。
# 事实上,uvloop要比nodejs、gevent等其他python异步框架至少要快2倍,性能可以比肩Go语言。
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
class InterfaceImpl():
# 创建AI测评任务的接口
@staticmethod
async def df(request):
loop = asyncio.get_event_loop()
# 另起一个非阻塞的线程运行任务,核心代码,花了两天查阅资料
loop.run_in_executor(None, os.system, "ping -c 5 127.1")
4、app.py
服务启动文件,监听在本地8080端口
# !/usr/bin/env python3
# -*- coding : utf-8 -*-
# @Time : 2020/11/6 15:57
# @Author : Attaboy
# @Email : slum2183@163.com
# @File : app.py
# @Software : PyCharm
import os
import sys
from sanic import Sanic
from bp import test_bp
# 添加sys.path路径
sys.path.append(os.getcwd())
app = Sanic(__name__)
app.blueprint(test_bp)
def entry_point():
app.run(host="127.0.0.1", port=8080, workers=2, debug=False)
if __name__ == '__main__':
entry_point()
三、测试
Ubuntu里启动该服务
tom@ros-ubuntu1804:~/project/python/test$ python app.py
[2021-02-19 22:26:46 +0800] [959] [INFO] Goin' Fast @ http://127.0.0.1:8080
[2021-02-19 22:26:46 +0800] [1004] [INFO] Starting worker [1004]
[2021-02-19 22:26:46 +0800] [1006] [INFO] Starting worker [1006]
Ubuntu本地curl接口三次,三次都立即返回响应信息,无阻塞。
tom@ros-ubuntu1804:~$ date ; for i in $(seq 3) ; do curl http://localhost:8080/df ; echo ; done ; date
Fri Feb 19 22:38:58 CST 2021
{"msg":"cmd running success"}
{"msg":"cmd running success"}
{"msg":"cmd running success"}
Fri Feb 19 22:38:58 CST 2021
查看服务控制台日志,耗时任务运行正常。
[2021-02-19 22:39:50 +0800] - (sanic.access)[INFO][127.0.0.1:38638]: GET http://localhost:8080/df 200 29
PING 127.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.019 ms
[2021-02-19 22:39:50 +0800] - (sanic.access)[INFO][127.0.0.1:38642]: GET http://localhost:8080/df 200 29
PING 127.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.009 ms
[2021-02-19 22:39:50 +0800] - (sanic.access)[INFO][127.0.0.1:38646]: GET http://localhost:8080/df 200 29
PING 127.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.010 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.034 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.023 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.014 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.034 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.016 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.010 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.034 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.017 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.009 ms
64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.031 ms
--- 127.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4059ms
rtt min/avg/max/mdev = 0.019/0.030/0.034/0.007 ms
64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.010 ms
--- 127.1 ping statistics ---
64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.016 ms
5 packets transmitted, 5 received, 0% packet loss, time 4068ms
rtt min/avg/max/mdev = 0.009/0.010/0.014/0.004 ms
--- 127.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4080ms
rtt min/avg/max/mdev = 0.009/0.016/0.023/0.005 ms