首页 > 解决方案 > 服务器端的阻塞和非阻塞调用,为什么异步客户端很重要?

问题描述

在 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

标签: python-3.xasynchronouspython-asyncioaiohttp

解决方案


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.


推荐阅读