Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用aiohttp的Python 3.6异步GET请求正在同步运行_Python_Python 3.x_Http_Python Asyncio_Aiohttp - Fatal编程技术网

使用aiohttp的Python 3.6异步GET请求正在同步运行

使用aiohttp的Python 3.6异步GET请求正在同步运行,python,python-3.x,http,python-asyncio,aiohttp,Python,Python 3.x,Http,Python Asyncio,Aiohttp,我有下面的功能正常,但由于某些原因,请求似乎是同步执行的,而不是异步执行的 我现在的假设是,这是因为主函数中的for record in recordsfor循环导致的,但我不确定如何更改它,以便请求可以异步执行。如果不是这样,我还需要改变什么 async def do_request(query_string): base_url = 'https://maps.googleapis.com/maps/api/place/textsearch/json?' pa

我有下面的功能正常,但由于某些原因,请求似乎是同步执行的,而不是异步执行的

我现在的假设是,这是因为主函数中的
for record in records
for循环导致的,但我不确定如何更改它,以便请求可以异步执行。如果不是这样,我还需要改变什么

async def do_request(query_string):
        base_url = 'https://maps.googleapis.com/maps/api/place/textsearch/json?'
        params = {'key': google_api_key,
                  'query': query_string}
        async with aiohttp.ClientSession() as session:
            async with session.request('GET', base_url, params=params) as resp:
                return resp


async def main():
    create_database_and_tables()
    records = prep_sample_data()[:100]

    for record in records:
        r = Record(record)

        if not r.is_valid:
            continue

        query_string = r.generate_query_string()

        resp = await do_request(query_string)
        print("NOW WRITE TO DATABASE")

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

您正在依次等待单独的
do\u request()
调用。不要直接等待它们(直到协同路由完成为止),而是使用事件循环并发运行它们:

async def main():
    create_database_and_tables()
    records = prep_sample_data()[:100]

    requests = []
    for record in records:
        r = Record(record)

        if not r.is_valid:
            continue

        query_string = r.generate_query_string()

        requests.append(do_request(query_string))

    for resp in asyncio.gather(*requests):
        print("NOW WRITE TO DATABASE")
asyncio.gather()
返回值是协同程序返回的所有结果的列表,其顺序与传递给
gather()
函数的顺序相同

如果您需要原始记录来处理响应,可以通过几种不同的方式将记录和查询字符串配对:

  • 将有效记录存储在单独的列表中,并在处理响应时使用
    zip()
    再次配对
  • 使用一个助手协同程序,它获取有效记录,生成查询字符串,调用请求,并将记录和响应作为元组一起返回
您还可以将响应处理混合到一个集合的协同程序中;获取记录、生成查询字符串、等待
do\u request
并在响应准备就绪时将结果存储在数据库中的一种


换言之,将需要连续进行的工作划分为协同工作,并将其收集起来。

以Martijn的答案为基础

如果请求的顺序对您来说不太重要(当它写入数据库时),您可以在获取命令时将响应写入数据库

编辑(解释更多):我在这里使用2个信号量。1是限制通过aiohttp的连接数。这将取决于您的系统。大多数linux系统默认为1024。根据我个人的经验,将其设置为低于OS max是更好的选择

max_coroutines
是为了解决一次运行过多coroutines的问题

我使用
asyncio。确保未来()
以便在构建列表时运行协同程序。这样,您就不会在执行任何协同路由之前创建完整的协同路由列表

# Limit the total number of requests you make by 512 open connections.
max_request_semaphore = asyncio.BoundedSemaphore(512)
max_coroutines = asyncio.BoundedSemaphore(10000)


async def process_response(response):
    print('Process your response to your database')


async def do_request(query_string):
    base_url = 'https://maps.googleapis.com/maps/api/place/textsearch/json?'
    params = {'key': google_api_key,
              'query': query_string}
    async with max_request_semaphore:
        async with aiohttp.ClientSession() as session:
            async with session.request('GET', base_url, params=params) as resp:
                return resp


# Excuse me for the bad function naming
async do_full_request(query_string):
    resp = await do_request(query_string)
    await process_response(resp)
    max_coroutines.release()

async def main():
    create_database_and_tables()
    records = prep_sample_data()[:100]

    requests = []
    for record in records:
        r = Record(record)

        if not r.is_valid:
            continue

        query_string = r.generate_query_string()

        # Will prevent more than 10k coroutines created.
        await max_coroutines.acquire()
        requests.append(
            asyncio.ensure_future(
                do_full_request(query_string)))

    # Now gather all the coroutines
    await asyncio.gather(*requests)


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

您正在等待循环中的每个响应,这是显式阻塞。您需要使用例如
asyncio.gather
进行重构,以并行发出请求-构建任务列表,然后执行所有任务。请参阅,例如@jornsharpe以这种方式重构是否意味着在每个记录的请求完成后,我将无法写入数据库?它看起来像是“asyncio.gather()`等待所有未来对象完成。如果您想为每个请求单独编写,您可以将其作为您计划的每个单独任务的一部分来完成。@jonrsharpe嗯,好吧,这已经变得相当混乱了@或者收集,然后处理返回列表中的响应。酷,这对我来说很有意义。我认为这将是有问题的130万需求,我需要使存储在内存中。有没有什么方法可以让每件事情都在运行中完成,而不是等待所有请求完成后再继续?@metersk:然后将响应的处理推到您收集的协同程序中。如果你要做那么多的请求,你可能想使用某种速率限制来防止网络过载。这听起来很理想,我会增加速率限制。这种模式会有多大的不同。我对这个问题有点困惑implementation@metersk:有关速率限制选项,请参阅。此外,创建130万个协同程序可能有点问题,可能需要成批创建。@MartijnPieters尽管我不是特别寻找它,但我发现这个问题非常有趣。直到