Python 异步asyncio Django命令按顺序运行

Python 异步asyncio Django命令按顺序运行,python,django,django-models,python-asyncio,django-commands,Python,Django,Django Models,Python Asyncio,Django Commands,我已经编写了一个简单的命令来循环遍历所有Result对象,并检查其www字段(表示已发布科学结果的URL,例如) 我们的数据库中有1M个结果,我想检查www字段,如果链接损坏,我将通过将实际doi添加到并保存它(在www字段中)来猜测它 这是我第一次使用asyncio,但我认为我的代码基本上是正确的,我不知道为什么代码会同步运行 主指挥部: #-*-编码:utf-8-*- 从未来导入unicode文字 导入异步 导入时间 从django.core.management.base导入BaseCom

我已经编写了一个简单的命令来循环遍历所有
Result
对象,并检查其
www
字段(表示已发布科学结果的URL,例如)

我们的数据库中有1M个结果,我想检查
www
字段,如果链接损坏,我将通过将实际
doi
添加到并保存它(在
www
字段中)来猜测它

这是我第一次使用asyncio,但我认为我的代码基本上是正确的,我不知道为什么代码会同步运行

主指挥部:

#-*-编码:utf-8-*-
从未来导入unicode文字
导入异步
导入时间
从django.core.management.base导入BaseCommand
从模型导入结果
def run_统计信息(结果数组、结果数):
正确URL的数量=0
总和检查时间=0
最大检查时间=0
对于\u结果的数组\u中的res:
如果res[0]:
正确URL的数量+=1
如果res[1]>最大检查时间:
最大检查时间=分辨率[1]
总和检查时间+=res[1]
返回f“”“对{num\u of_results}结果运行统计信息\n
----------------------------------------------------------------------------
正确/损坏的链接比率:{num_of_correct_url}/{num_of_results-num_of_correct_url}\n
检查URL的平均时间:{sum_check_time/num_of_results}\n
"""
类命令(BaseCommand):
help='检查结果的www字段中的url,如果链接没有响应,则尝试生成新的超链接'\
“(使用DOI)并将其保存在www_processed字段”
异步def运行检查(自身、obj):
"""
负责检查结果。
`wait obj.get_www()`将函数控制传递回事件循环。
:返回
在未更改的url上为True
否则就错了
"""
打印('STARTING run\u check',file=self.stdout)
开始时间=时间。性能计数器()
final_url=wait obj.get_www_corroutine()
如果final_url==obj.www:
打印('STOPPING run\u check',file=self.stdout)
返回True,time.perf_counter()-开始时间
其他:
打印('STOPPING run\u check',file=self.stdout)
返回False,time.perf_counter()-开始时间
异步def主(自身、objs):
等待asyncio.gather(self.run\u检查(objs[0]),self.run\u检查(objs[1]))
def句柄(自身、*args、**kwargs):
开始时间=时间。性能计数器()
打印('已启动进程',file=self.stdout)
objs=Result.objects.all()
num_of_results=10#Result.objects.all().count()
打印('running main',file=self.stdout)
异步定义主例程():
数组\u of_responses=wait asyncio.gather(*(objs中的u的self.run\u检查(u))
打印(f'retrieved{num\u of_results}results,running command',file=self.stdout)
#打印(res_数组,file=self.stdout)
打印(运行统计信息(响应数组,10)+f'总时间:{time.perf_counter()-start_time}\n',
文件=self.stdout)
asyncio.run(\u main\u routine())
检查www字段并保存猜测链接(如果需要)的方法

async def get_www_corroutine(self):
如果不是self.www\u last\u checked或datetime.date.today()-self.www\u last\u checked>datetime.timedelta(天=365):
如果不是self.www或不是wait check\u url\u返回\u 200\u in\u time\u corroutine(self.www):#www已损坏
如果self.doi:
self.www_processed=self.get_doi_url()
其他:
self.www_processed=无
self.www_last_checked=datetime.date.today()
否则:#www看起来不错
self.www_processed=self.www
self.save()
返回self.www\u已处理或错误
用于检查链接是否返回200的方法

async def check\u url\u返回\u 200\u in\u time\u协程(url,timeout=1):
尝试:
与aiohttp.ClientSession()作为会话异步:
以session.get(url)作为响应的异步:
返回response.status==200
除aiohttp.client_exceptions.InvalidURL外:
返回错误
实际产出:

started the process

running main

STARTING run_check

STOPPING run_check

STARTING run_check

STOPPING run_check

retrieved 10 results, running command

ran statistics on 10 results 

            ----------------------------------------------------------------------------
            correct/corrupted link ratio: 1 / 9

            Mean time to check URL: 0.17720807899999896

            total time: 73.279784077

正如您所看到的,代码是按顺序执行的,需要很长时间才能完成。我希望看到
首先为所有对象启动运行检查
,然后
停止运行检查

我终于解决了这个问题

代码异步运行(我只测试了两个结果,因此输出结果不清楚)


瓶颈实际上是db查询,
objs=Result.objects.all()。仅('www','www\u processed','www\u last\u checked')。order\u by('?')[:2]
需要很多时间,因为有一百万个对象,
order\u by(?)
需要先做一些逻辑。更多信息:

至少乍一看,您的代码看起来是正确的。您可以打印您测试的URL吗?正确的/损坏的比率非常有利于损坏-可能URL无效,在这种情况下,aiohttp可能在阻止任何内容之前引发异常,因此
wait
从不将控制权放弃给事件循环?此外,打印结果计数的代码似乎不正确,因为它获得了“结果数”来自源中的硬编码编号;实际上,它只启动两次
run\u check
,其中一次成功。我假设失败的是首先返回的,并且代码路径不等待任何东西,这就是为什么这两个没有交织在一起。