HOME/Articles/

concurrent_requests

Article Outline

Example Python program concurrent_requests.py Python version 3.x or newer. To check the Python version use:

python --version

Modules

  • import asyncio
  • import multiprocessing.pool
  • import time
  • import requests
  • import aiohttp
  • import async_timeout
  • import uvloop

Methods

  • def serial(urls=URLS, session=None):
  • def parallel(urls=URLS, session=None):
  • async def async_get(url, session, timeout=10):
  • async def async_get_all(urls):
  • def asynchronous(urls=URLS, loop=None):
  • def main():

Code

Python example

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Making concurrent HTTP requests.

Uses `requests` library which you can get with:

    $ pip install requests

Additionally, to make HTTP requests asynchronously you have to install these
packages:

    $ pip install aiohttp cchardet aiodns uvloop

"""

import asyncio
import multiprocessing.pool
import time

import requests

try:
    import aiohttp
    import async_timeout
except ImportError:
    aiohttp = None
    async_timeout = None
try:
    import uvloop
except ImportError:
    uvloop = None


URLS = [f'https://httpbin.org/get?id={x}' for x in range(25)]


def serial(urls=URLS, session=None):
    """Make HTTP requests in series."""
    session = session if session else requests
    return (session.get(url).json() for url in urls)


def parallel(urls=URLS, session=None):
    """Make HTTP requests in parallel using 8 worker threads."""
    num_threads = 8
    session = session if session else requests
    with multiprocessing.pool.ThreadPool(num_threads) as pool:
        responses = pool.imap(session.get, urls)
        for response in responses:
            yield response.json()


async def async_get(url, session, timeout=10):
    """Make one asynchronous HTTP GET request and return its JSON content."""
    with async_timeout.timeout(timeout):
        async with session.get(url) as response:
            return await response.json()


async def async_get_all(urls):
    """Make many asynchronous HTTP GET requests and return all their JSON
    content.
    """
    async with aiohttp.ClientSession() as session:
        tasks = [async_get(url, session) for url in urls]
        return await asyncio.gather(*tasks)


def asynchronous(urls=URLS, loop=None):
    """Fetch all URLs in an event loop asynchronously."""
    loop = loop if loop else asyncio.get_event_loop()
    return loop.run_until_complete(async_get_all(urls))


def main():
    _ = '=' * 4

    session = requests.Session()

    print(f'{_} SERIAL (no session persitence) {_}')
    start = time.time()
    for response in serial():  # no session
        print(response)
    end = time.time() - start
    print('Done in {0:.3f}s'.format(end))
    print()

    print(f'{_} SERIAL (persistent session) {_}')
    start = time.time()
    for response in serial(session=session):
        print(response)
    end = time.time() - start
    print('Done in {0:.3f}s'.format(end))
    print()

    print(f'{_} PARALLEL (no session persistence) {_}')
    start = time.time()
    for response in parallel():  # no session
        print(response)
    end = time.time() - start
    print('Done in {0:.3f}s'.format(end))
    print()

    print(f'{_} PARALLEL (persistent session) {_}')
    start = time.time()
    for response in parallel(session=session):
        print(response)
    end = time.time() - start
    print('Done in {0:.3f}s'.format(end))
    print()

    # the uvloop was faster than vanilla event loop
    # before Python 3.6 and recent aiohttp releases
    print(f'{_} ASYNC (uvloop, persistent session) {_}')
    if aiohttp and uvloop:
        loop = uvloop.new_event_loop()
        start = time.time()
        for response in asynchronous(loop=loop):
            print(response)
        end = time.time() - start
        print('Done in {0:.3f}s'.format(end))
    else:
        print('Try installing the uvloop Python package')
        print('\t$ pip install uvloop')
    print()

    print(f'{_} ASYNC (vanilla loop, persistent session) {_}')
    if aiohttp:
        loop = asyncio.get_event_loop()
        start = time.time()
        for response in asynchronous(loop=loop):
            print(response)
        end = time.time() - start
        print('Done in {0:.3f}s'.format(end))
    else:
        print('Try installing the aiohttp Python package')
        print('\t$ pip install aiohttp')
    print()


if __name__ == '__main__':
    main()