目录
1. adrf库
链接: pypi
基本介绍
- 介绍
Async support for Django REST framework
对 Django REST 框架的异步支持- 要求
Requirements
Python 3.8+
Django 4.1+
2. django
2.1. django3.2(此版本只能普通函数实现异步,连接数据库操作不支持异步)
当前环境不满足条件,所以不能直接使用adrf
2.1.1. 获取代码
- 新建python虚拟环境(为了获取adrf中的代码,避免把项目环境中的django更新)
- 执行pip install adrf==0.1.3
- 进入adrf的packages,目录如下:
不包含async_viewsets.py,后续会提到。- 在当前django3.2项目的apps目录下新建adrf文件夹,将另外6个文件复制进去
2.1.2. 编写逻辑
1. async_viewsets.py
文件位置:apps/adrf/async_viewsets.py
在apps/adrf/viewsets.py中已经实现了异步viewsets,但是每次需要继承多个父类比较费劲,直接继承好得了。
from apps.adrf.viewsets import ViewSet as AsyncViewSet
from rest_framework import generics, mixins, views
class AsyncGenericViewSet(AsyncViewSet, generics.GenericAPIView):
# 以后凡是用GenericViewSet的地方都可以用此类替换
pass
2. serializer
文件位置:apps/app1/serializers.py
from apps.adrf.serializers import Serializer as AsyncSerializer
class QASerializer(AsyncSerializer):
"""知识问答历史记录格式"""
role = serializers.CharField(required=True, max_length=32, allow_null=False, allow_blank=False,
help_text='角色')
content = serializers.CharField(required=True, max_length=1024, allow_null=False, allow_blank=False,
help_text='内容')
class Meta:
fields = ['role', 'content']
3. views.py
文件位置:apps/app1/views.py
import asyncio
from asgiref.sync import sync_to_async
from apps.adrf.async_viewsets import AsyncGenericViewSet # 2.1中自己写的
class DataKnowledgeViewSet(mixins.RetrieveModelMixin, AsyncGenericViewSet):
queryset = DataKnowledge.objects.all()
serializer_class = DataKnowledgeModelSerializer
def get_serializer_class(self):
if self.action in ['qa1']:
self.serializer_class = QASerializer
return self.serializer_class
@swagger_auto_schema(operation_description="知识问答",
operation_summary='知识问答',
tags=['知识'],
responses={**SWAGGER_RESPONSE_MESSAGE, 200: 'ok'})
@action(methods=['POST'], detail=False, url_path='qa1', url_name='qa1')
async def qa1(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
data = await serializer.adata # data = serializer.data 响应时间差不多
await asyncio.sleep(2)
return Response(data)
@swagger_auto_schema(operation_description="知识问答",
operation_summary='知识问答',
tags=['知识'],
responses={**SWAGGER_RESPONSE_MESSAGE, 200: 'ok'})
@action(methods=['POST'], detail=False, url_path='qa2', url_name='qa2')
async def qa2(self, request, *args, **kwargs):
msg = random.randint(0,99)
await asyncio.sleep(2)
return Response(msg)
@swagger_auto_schema(operation_description="知识问答",
operation_summary='知识问答',
tags=['知识'],
responses={**SWAGGER_RESPONSE_MESSAGE, 200: 'ok'})
@action(methods=['POST'], detail=False, url_path='qa3', url_name='qa3')
def qa3(self, request, *args, **kwargs):
msg = random.randint(0, 99)
return Response(msg)
2.1.3. 测试
不能用requests测不出来。。
import time
import asyncio
import aiohttp
import json
async def fetch(session):
payload = json.dumps([
{
"role": "user",
"content": "dsm出现了红色错误"
}
])
headers = {
'Content-Type': 'application/json'
}
url1 = "http://127.0.0.1:8001/cn/api/data-knowledge/qa1/"
url2 = "http://127.0.0.1:8001/cn/api/data-knowledge/qa2/"
url3 = "http://127.0.0.1:8001/cn/api/data-knowledge/qa3/"
async with session.post(url1, data=payload, headers=headers) as response:
return await response.text()
async def xxx():
async with aiohttp.ClientSession() as session:
html = await fetch(session)
print(html)
async def main(req_count=10):
tasks = []
for i in range(0, req_count):
task = asyncio.create_task(xxx())
tasks.append(task)
await asyncio.wait(tasks)
# 运行异步任务
x1 = time.time()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
x2 = time.time()
print(x2 - x1)
###结果
# url1
{"message":"OK","code":200,"results":[{"role":"user","content":"dsm出现了红色错误"}]}
{"message":"OK","code":200,"results":[{"role":"user","content":"dsm出现了红色错误"}]}
{"message":"OK","code":200,"results":[{"role":"user","content":"dsm出现了红色错误"}]}
{"message":"OK","code":200,"results":[{"role":"user","content":"dsm出现了红色错误"}]}
{"message":"OK","code":200,"results":[{"role":"user","content":"dsm出现了红色错误"}]}
{"message":"OK","code":200,"results":[{"role":"user","content":"dsm出现了红色错误"}]}
{"message":"OK","code":200,"results":[{"role":"user","content":"dsm出现了红色错误"}]}
{"message":"OK","code":200,"results":[{"role":"user","content":"dsm出现了红色错误"}]}
{"message":"OK","code":200,"results":[{"role":"user","content":"dsm出现了红色错误"}]}
{"message":"OK","code":200,"results":[{"role":"user","content":"dsm出现了红色错误"}]}
2.2377092838287354
# url2
{"message":"OK","code":200,"results":92}
{"message":"OK","code":200,"results":95}
{"message":"OK","code":200,"results":55}
{"message":"OK","code":200,"results":64}
{"message":"OK","code":200,"results":13}
{"message":"OK","code":200,"results":81}
{"message":"OK","code":200,"results":83}
{"message":"OK","code":200,"results":28}
{"message":"OK","code":200,"results":2}
{"message":"OK","code":200,"results":8}
2.2307043075561523
# url3同步函数正常返回
2.2 django4.2及以上
连接数据库自带异步???adrf存在的意义?
普通函数还是可以通过adrf进行异步返回
async def qa2(self, request, *args, **kwargs):
msg = random.randint(0,99)
await asyncio.sleep(2)
return Response(msg)
2.2.1 daphne启动 同步函数
ModelSerializer
def retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)
单个请求:0.8s
50个请求:2.4s
2.2.2 runserver启动同步函数
ModelSerializer
def retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)
1个请求:0.8s
50个请求:1.8s
2.2.3 dephne启动异步函数
AsyncModelSerializer
async def retrieve(self, request, *args, **kwargs):
instance = await self.aget_object()
serializer = self.get_serializer(instance)
data = await serializer.adata
return Response(data)
单个请求:0.8s
50个请求:2.4s
3. 启动项目
runserver开发阶段支持异步,如果想同步那么把下面INSTALLED_APPS 中的daphne注释
3.1. daphne
安装:pip install daphne
配置:settings.py
INSTALLED_APPS = [
...
'daphne', # 需要靠前位置
...
]
...
ASYNC_MODE = 'django' # 不知道啥用
ASGI_APPLICATION = 'service_brain_model.asgi.application'
启动:daphne service_brain_model.asgi:application -h 0.0.0.0 -p 8001
3.2. pycharm配置方式(windows)
如有问题欢迎指正