Python web框架中如何并发请求多个外部URL?

语言(python)
现在有这么一种情况。

web 框架 响应 来自客户端的请求的 handler 里面会返回一个列表记录 list1, 对于 list1 的第一个元素 ele1 要请求 url1, list1 的第二个元素 ele2 要请求 url2, 以此类推,平均一个 url 请求要 200-300ms ,超时调成 1s 所以如果 list1 的长度为 10 的话,如果迭代请求的话,总时间为 5S 左右。 你们是怎么处理这种情况的?


Python web框架中如何并发请求多个外部URL?

13 回复

那就并发请求然后 wait 到所有请求完成呗


在Python web框架里并发请求多个外部URL,最直接高效的方式是用asyncio配合aiohttp。如果你的框架本身支持异步(比如FastAPI、Sanic),或者你在用异步中间件,这方法最合适。下面是个在异步视图函数里用的例子:

import asyncio
import aiohttp
from fastapi import FastAPI

app = FastAPI()

async def fetch_url(session: aiohttp.ClientSession, url: str):
    try:
        async with session.get(url) as response:
            return await response.text()
    except Exception as e:
        return f"Error fetching {url}: {e}"

@app.get("/fetch-multiple")
async def fetch_multiple_urls():
    urls = [
        "https://httpbin.org/get",
        "https://api.github.com",
        "https://jsonplaceholder.typicode.com/posts/1"
    ]
    
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_url(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
    
    return {"results": results}

如果你的项目是传统的同步框架(比如Flask、Django),并且不想动整体结构,可以用concurrent.futuresThreadPoolExecutor来模拟并发。注意,这只是用线程池绕过GIL的I/O阻塞,不是真异步。

from concurrent.futures import ThreadPoolExecutor, as_completed
import requests
from flask import Flask, jsonify

app = Flask(__name__)

def fetch_url_sync(url):
    try:
        return requests.get(url).text
    except Exception as e:
        return f"Error: {e}"

@app.route("/fetch-multiple")
def fetch_multiple():
    urls = ["https://httpbin.org/get", "https://api.github.com"]
    results = []
    with ThreadPoolExecutor(max_workers=5) as executor:
        future_to_url = {executor.submit(fetch_url_sync, url): url for url in urls}
        for future in as_completed(future_to_url):
            results.append(future.result())
    return jsonify(results)

选异步方案还是线程池,主要看你项目的基础架构和需求。

这个时候适合用 tornado 的 ayncHttpClient 啦 http://www.tornadoweb.org/en/stable/httpclient.html#tornado.httpclient.AsyncHTTPClient

接受传入 loop, 非 tornado 框架也可以使用
比如

用 multiprocess?

gevent 的话用 grequests

如果你的框架是 tornado , 可以用它的 AsyncHttpClient
如果你的框架是 flask ,把这些 url 请求任务丢到 celery ,把请求情况保存到 redis ,所有请求都完成了再回调

并发请求之后合并结果

如果是因为网慢或者 response 大的话。。。即使请求并发了,结果还是快不起来

这种情况的话,我们一般用这个 [Go.IoT]( https://goiot.cc) 做一个中间件,
可以同时触发多个 http 请求,全都完成后执行下一步。

参考:[异步多线请求]( https://bb.goiot.cc/uploads/files/1487329416416-flow.png)

效果是你那种效果,难道 golang 也需要中间件来完成这种功能吗

当然不是。这个东东适用于那种需要经常更改的逻辑,如果是写好放在那很久都不会懂它,自然用原生的组件来完成会更漂亮,比如 golang 。

多线程也行啊

难道不是 gevent?

回到顶部