快速理解请求-同步与异步

3 阅读2分钟

同步:一条队列排队打饭一个一个来

异步:开设多条队列,分批排队打饭

代码示例使用主流的httpx

同步:

import httpx
import time

def fetch_sync(client, url):
    response = client.get(url)
    return response.status_code

def main():
    url = "https://httpbin.org/delay/1"  # 模拟延迟 1 秒
    times = 5
    
    start = time.time()
    with httpx.Client() as client:
        for i in range(times):
            print(f"正在同步请求 {i+1}/{times}...")
            try:
                status = fetch_sync(client, url)
                print(f"请求 {i+1} 完成,状态码:{status}")
            except Exception as e:
                print(f"请求 {i+1} 失败:{e}")
    end = time.time()
    
    print(f"\n✅ 同步总耗时:{end - start:.2f} 秒")
    print(f"📊 平均每个请求耗时:{(end - start) / times:.2f} 秒")

if __name__ == "__main__":
    main()

运行结果: 正在同步请求 1/5... 请求 1 完成,状态码:200 正在同步请求 2/5... 请求 2 完成,状态码:200 正在同步请求 3/5... 请求 3 完成,状态码:200 正在同步请求 4/5... 请求 4 完成,状态码:200 正在同步请求 5/5... 请求 5 完成,状态码:200

✅ 同步总耗时:13.32 秒 📊 平均每个请求耗时:2.66 秒

异步:

import httpx
import asyncio
import time

async def fetch_async(client, url, sem):
    # sem 是信号量,用于控制并发数量,防止瞬间请求太多被封 IP
    async with sem:
        try:
            # httpx 的异步请求方式
            response = await client.get(url, timeout=30.0)
            status = response.status_code
            print(f"请求完成,状态码:{status}")
            return status
        except httpx.TimeoutException as e:
            print(f"请求超时:{e}")
            return None
        except httpx.RequestError as e:
            print(f"请求失败:{e}")
            return None

async def main():
    url = "https://httpbin.org/delay/1"
    times = 5
    
    # 创建信号量,限制最大并发数为 5
    sem = asyncio.Semaphore(5)
    
    start = time.time()
    
    # limits 参数可以控制连接池大小
    limits = httpx.Limits(max_keepalive_connections=5, max_connections=10)
    
    async with httpx.AsyncClient(limits=limits) as client:
        # 创建任务列表
        tasks = []
        for i in range(times):
            print(f"正在发起异步请求 {i+1}/{times}...")
            task = asyncio.create_task(fetch_async(client, url, sem))
            tasks.append(task)
        
        # 等待所有任务完成
        await asyncio.gather(*tasks)
        
    end = time.time()
    print(f"\n✅ 异步总耗时:{end - start:.2f} 秒")
    print(f"📊 平均每个请求耗时:{(end - start) / times:.2f} 秒")

if __name__ == "__main__":
    asyncio.run(main())

运行结果 正在发起异步请求 1/5... 正在发起异步请求 2/5... 正在发起异步请求 3/5... 正在发起异步请求 4/5... 正在发起异步请求 5/5... 请求完成,状态码:200 请求完成,状态码:200 请求完成,状态码:200 请求完成,状态码:200 请求完成,状态码:200

✅ 异步总耗时:6.33 秒 📊 平均每个请求耗时:1.27 秒

得出结论:

异步请求效率高,完成耗时短