Блокирующие и неблокирующие вызовы на стороне сервера, почему это важно для асинхронной стороны клиента? - PullRequest
1 голос
/ 08 января 2020

Экспериментируя с некоторым асинхронным кодом, в Python 3.8.0 я наткнулся на следующую ситуацию. У меня есть client.py, который может обрабатывать соединения асинхронно с сервером в server.py. Этот сервер делает вид, что выполняет некоторую работу, но на самом деле спит несколько секунд, а затем возвращается. Мой вопрос заключается в том, что, поскольку сервер работает в совершенно другом процессе, почему имеет значение, блокирует ли спящий метод или нет, и если процессы на стороне сервера могут не блокировать, каково преимущество таких асинхронных вызовов в первое место?

# 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. сон (неблокирующий)

elapsed_concurrent = 3,02 и sum_sleeps = 19,00

time.sleep (блокировка)

elapsed_concurrent = 19,04 и sum_sleeps = 19.00

1 Ответ

1 голос
/ 11 января 2020

Хотя сервер работает в совершенно другом процессе, он не может принимать несколько активных соединений одновременно, как многопоточный сервер. Таким образом, клиент и сервер работают асинхронно, оба имеют свое собственное событие l oop.

Сервер может принимать новые подключения от клиента только тогда, когда событие l oop приостановлено в неблокирующем спящем режиме. , Создается впечатление, что сервер многопоточный, но на самом деле быстро чередуется между доступными соединениями. Блокирующий спящий режим сделает запросы последовательными, потому что приостановленное событие l oop будет бездействовать и не сможет обрабатывать новые соединения в это время.

...