Asynchronous downloads using asyncio in Python

asyncio is especially useful for I/O-bound tasks like downloading or uploading files, where the primary bottleneck is neither CPU nor Memory but rather waiting for network responses.

asyncio is part of the standard lib for python3 but you need to install aiohttp by running pip install aiohttp.

1. Files to download

Our research project will attempt to download about 200k images of the Earth from the NASA Photo Journal.

2. Prepare directory to save the files

3. Async download function

  • aiohttp.ClientSession to handle HTTP requests asynchronously.
  • Read the response content asynchronously and write to a file.

4. Main function to manage the downloads

  • Create a list of tasks for each URL and run them concurrently using asyncio.gather.

5. Running the main function

Note: Asyncio uses a single-threaded event loop and is therefore ideal for I/O-bound tasks. Threading however, uses multiple threads running in parallel making it suitable for CPU-bound tasks.

Improvements

We can consider a few improvements:

  1. Ensure that aiohttp is using a connection pool to manage multiple concurrent connections efficiently.
  2. Add error handling to manage cases where some downloads might fail due to intermittent issues. Perhaps add some retry logic also?
  3. Use a semaphore to limit the number of concurrent downloads to avoid overwhelming the server and risk of DDoS.
  4. Although writing the file to disk is typically fast, we can ensure it’s not blocking the event loop by using asynchronous file writing.
  5. Finally, we can handle the return values from asyncio.gather to collect results or exceptions
  • aiohttp_retry is used to automatically retry failed requests.
  • ExponentialRetry sets up exponential backoff for retries, attempting up to 5 retries.
  • Logging is configured for better visibility of the process and errors.
  • Increased the number of concurrent downloads to 10 for potentially better performance.
  • asyncio.gather is used with return_exceptions=True to handle exceptions for each task. This allows the program to continue running even if some downloads fail.
  • Added logging to surface download success and failure information.
  • RetryClient ensures that transient errors are retried with exponential backoff to make for a robust app.

Further Reading