Python tutorials > Working with External Resources > Networking > How to do asynchronous networking?
How to do asynchronous networking?
This tutorial explores asynchronous networking in Python using the asyncio
library. Asynchronous networking allows your program to perform other tasks while waiting for network operations to complete, improving responsiveness and efficiency, especially in I/O-bound applications. We'll cover the fundamental concepts, provide code examples, and discuss best practices.
Introduction to Asynchronous Networking
Asynchronous programming enables a single-threaded process to handle multiple concurrent operations without blocking. In the context of networking, this means your application can initiate a network request and continue executing other code while waiting for the response. The asyncio
library provides the tools to implement this in Python.
Basic Asynchronous Client Example
This snippet demonstrates a basic asynchronous TCP client.
asyncio.open_connection()
establishes a connection to the server asynchronously, returning reader and writer objects.writer.write()
sends the message to the server.await writer.drain()
flushes the buffer to ensure data is sent.reader.read()
reads data from the server asynchronously.writer.close()
and await writer.wait_closed()
.asyncio.run(main())
executes the asynchronous main function.
import asyncio
async def tcp_echo_client(message, host='127.0.0.1', port=8888):
reader, writer = await asyncio.open_connection(host, port)
print(f'Send: {message!r}')
writer.write(message.encode())
await writer.drain()
data = await reader.read(100)
print(f'Received: {data.decode()!r}')
print('Close the connection')
writer.close()
await writer.wait_closed()
async def main():
await tcp_echo_client('Hello, Server!')
if __name__ == '__main__':
asyncio.run(main())
Basic Asynchronous Server Example
This snippet demonstrates a basic asynchronous TCP server.
asyncio.start_server()
starts the server, listening for incoming connections. It takes a coroutine function (handle_echo
in this case) that will be executed for each new connection.handle_echo()
reads data from the client, prints it, sends it back, and then closes the connection.server.serve_forever()
keeps the server running indefinitely, handling incoming connections. The async with server:
statement ensures the server is properly closed when the script exits.
import asyncio
async def handle_echo(reader, writer):
data = await reader.read(100)
message = data.decode()
addr = writer.get_extra_info('peername')
print(f'Received {message!r} from {addr!r}')
print(f'Send: {message!r}')
writer.write(data)
await writer.drain()
print('Close the connection')
writer.close()
async def main():
server = await asyncio.start_server(
handle_echo,
'127.0.0.1',
8888
)
addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets)
print(f'Serving on {addrs}')
async with server:
await server.serve_forever()
if __name__ == '__main__':
asyncio.run(main())
Concepts Behind the Snippets
The core concept is the use of async
and await
keywords.
async
defines a coroutine, which is a special type of function that can be paused and resumed.await
suspends the execution of the coroutine until the awaited task completes. This allows the event loop to execute other tasks while waiting for I/O operations to finish.asyncio
. It manages the execution of coroutines and handles I/O events.
Real-Life Use Case: Concurrent Web Requests
This example demonstrates how to perform multiple web requests concurrently using aiohttp
, an asynchronous HTTP client library.
aiohttp.ClientSession()
creates a session to manage HTTP connections.fetch_url()
fetches the content of a URL asynchronously.asyncio.gather()
runs multiple coroutines concurrently and returns a list of their results.
import asyncio
import aiohttp
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
urls = [
'https://www.example.com',
'https://www.python.org',
'https://www.google.com'
]
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks)
for url, result in zip(urls, results):
print(f'Downloaded {url}: {len(result)} bytes')
if __name__ == '__main__':
asyncio.run(main())
Best Practices
try...except
blocks within your coroutines.asyncio.Task.cancel()
.async with
statements where applicable.aiohttp
instead of requests
).
Interview Tip
When discussing asynchronous programming in interviews, emphasize the benefits in I/O-bound scenarios. Be prepared to explain the difference between concurrency and parallelism, and how asyncio
achieves concurrency using a single thread and an event loop. Also, be ready to discuss common pitfalls like blocking the event loop with synchronous code.
When to Use Asynchronous Networking
Asynchronous networking is particularly beneficial in the following scenarios:
Memory Footprint
Asynchronous programming can sometimes reduce memory footprint compared to traditional multi-threading because it avoids the overhead of creating and managing multiple threads. However, each coroutine still consumes memory, especially if it holds large amounts of data. Be mindful of the number of active coroutines and the amount of data they process concurrently. Using generators and iterators can also help to reduce memory usage when processing large datasets.
Alternatives to asyncio
While asyncio
is the standard library for asynchronous programming in Python, other alternatives exist:
Pros of Asynchronous Networking
Cons of Asynchronous Networking
FAQ
-
What is the difference between concurrency and parallelism?
Concurrency is the ability to deal with multiple tasks at the same time, but not necessarily execute them simultaneously. Parallelism is the ability to execute multiple tasks simultaneously.asyncio
provides concurrency through cooperative multitasking within a single thread, while parallelism typically involves multiple threads or processes. -
How do I handle exceptions in asynchronous code?
Usetry...except
blocks within your coroutines to catch and handle exceptions. You can also useasyncio.Task.cancel()
to cancel tasks and handleasyncio.CancelledError
. -
Can I use regular blocking functions in asynchronous code?
It's generally not recommended to use regular blocking functions directly within asynchronous code, as they can block the event loop and negate the benefits of asynchronous programming. If you need to use blocking functions, run them in a separate thread usingasyncio.to_thread()
orexecutor.run()
.