python-3.x - 服务器端的阻塞和非阻塞调用,为什么异步客户端很重要?
问题描述
在 Python 3.8.0 中尝试一些异步代码时,我偶然发现了以下情况。我有client.py
它可以异步处理与服务器的连接server.py
。该服务器假装做一些工作,但实际上休眠了几秒钟然后返回。我的问题是,既然服务器在一个完全不同的进程中运行,为什么 sleep 方法是否阻塞以及如果服务器端的进程可能没有阻塞,那么执行这样的异步调用有什么好处呢?第一名?
# client.py
import time
import asyncio
import aiohttp
async def request_coro(url, session):
async with session.get(url) as response:
return await response.read()
async def concurrent_requests(number, url='http://localhost:8080'):
tasks = []
async with aiohttp.ClientSession() as session:
for n in range(number):
# Schedule the tasks
task = asyncio.create_task(request_coro(url, session))
tasks.append(task)
# returns when all tasks are completed
return await asyncio.gather(*tasks)
t0 = time.time()
responses = asyncio.run(concurrent_requests(10))
elapsed_concurrent = time.time() - t0
sum_sleeps = sum((int(i) for i in responses))
print(f'{elapsed_concurrent=:.2f} and {sum_sleeps=:.2f}')
# server.py
import time
import random
import logging
import asyncio
from aiohttp import web
random.seed(10)
async def index(requests):
# Introduce some latency at the server side
sleeps = random.randint(1, 3)
# NON-BLOCKING
# await asyncio.sleep(sleeps)
# BLOCKING
time.sleep(sleeps)
return web.Response(text=str(sleeps))
app = web.Application()
app.add_routes([web.get('/', index),
web.get('/index', index)])
logging.basicConfig(level=logging.DEBUG)
web.run_app(app, host='localhost', port=8080)
这些是客户端使用阻塞或非阻塞睡眠方法进行的 10 次异步调用的结果:
asyncio.sleep(非阻塞)
elapsed_concurrent=3.02 and sum_sleeps=19.00
time.sleep (blocking)
elapsed_concurrent=19.04 and sum_sleeps=19.00
解决方案
Although the server is running in a completely different process, it can not take multiple active connections at the same time, like a multi threaded server. So the client and the server are working asynchonously both having their own event loop.
The server can only take new connections from the client when the event loop is suspended in a non-blocking sleep. Making it appear that the server is multi threaded but actually rapidly alternates between available connections. A blocking sleep will make the requests sequential because the suspended event loop will sit idle and can not handle new connections in the mean time.
推荐阅读
- ios - SwiftUI:如何确定视图是在 NavigationView、Sheet 中呈现还是在根目录中?
- ms-access - 如何对每个标题的 Access 报表进行排序,类似于 Excel 数据透视表?
- django - Django 在多对一关系中引用特定对象
- javascript - 在html中询问数字,在js中添加,在html中打印
- laravel - 如何设置双级深度或 where 子句
- bash - 当我 docker run --mount bashrc 时,bashrc 文件不起作用
- python - 用另一个数组过滤 numpy 数组的最快方法是什么?
- python - openpyxl 的 load_workbook 和 .value 函数输入错误
- rust - 如何从rust中的pdf中提取字符串?
- javascript - 比较 2 个对象时如何优化慢速“for 循环”?