Building a High‑Performance HTTP Keep‑Alive Benchmark with Python asyncio

This article explains the concept of asynchronous I/O, introduces Python’s provisional asyncio module, and walks through a complete example that creates an HTTP keep‑alive benchmark tool, including code, test environment, performance results, and key asyncio concepts such as event loops, futures, coroutines, and generators.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
Building a High‑Performance HTTP Keep‑Alive Benchmark with Python asyncio

Asynchronous operations are a common concept in computer systems because collaborating entities often have different processing speeds; in software development the mismatch between CPU and I/O speeds makes asynchronous I/O ubiquitous, appearing in browsers, Node.js, and other frameworks. This article focuses on Python asynchronous I/O.

Python 3.4 introduced the asyncio module to support asynchronous I/O. Although its API is provisional and may change, asyncio is already powerful and worth learning.

Example

asyncio is mainly used for TCP/UDP socket communication, managing many connections without creating a thread per connection. The following example adapts an official snippet to implement an HTTP keep‑alive benchmark tool for diagnosing a web server’s handling capacity.

Functionality overview: every 10 ms create 10 connections (up to a target like 10 k); each connection periodically sends a HEAD request to keep the HTTP keep‑alive alive.

Code

import argparse
import asyncio
import functools
import logging
import random
import urllib.parse

loop = asyncio.get_event_loop()

@asyncio.coroutine
def print_http_headers(no, url, keepalive):
    url = urllib.parse.urlsplit(url)
    wait_for = functools.partial(asyncio.wait_for, timeout=3, loop=loop)
    query = ('HEAD {url.path} HTTP/1.1
'
             'Host: {url.hostname}
'
             '
').format(url=url).encode('utf-8')
    rd, wr = yield from wait_for(asyncio.open_connection(url.hostname, 80))
    while True:
        wr.write(query)
        while True:
            line = yield from wait_for(rd.readline())
            if not line:  # end of connection
                wr.close()
                return no
            line = line.decode('utf-8').rstrip()
            if not line:  # end of header
                break
            logging.debug('(%d) HTTP header> %s' % (no, line))
            yield from asyncio.sleep(random.randint(1, keepalive//2))

@asyncio.coroutine
def do_requests(args):
    conn_pool = set()
    waiter = asyncio.Future()
    def _on_complete(fut):
        conn_pool.remove(fut)
        exc, res = fut.exception(), fut.result()
        if exc is not None:
            logging.info('conn#{} exception'.format(exc))
        else:
            logging.info('conn#{} result'.format(res))
        if not conn_pool:
            waiter.set_result('event loop is done')
    for i in range(args.connections):
        fut = asyncio.async(print_http_headers(i, args.url, args.keepalive))
        fut.add_done_callback(_on_complete)
        conn_pool.add(fut)
        if i % 10 == 0:
            yield from asyncio.sleep(0.01)
    logging.info((yield from waiter))

def main():
    parser = argparse.ArgumentParser(description='asyncli')
    parser.add_argument('url', help='page address')
    parser.add_argument('-c', '--connections', type=int, default=1,
                        help='number of connections simultaneously')
    parser.add_argument('-k', '--keepalive', type=int, default=60,
                        help='HTTP keepalive timeout')
    args = parser.parse_args()
    logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
    loop.run_until_complete(do_requests(args))
    loop.close()

if __name__ == '__main__':
    main()

Testing and Analysis

Hardware: CPU 2.3 GHz / 2 cores, RAM 2 GB

Software: CentOS 6.5 (kernel 2.6.32), Python 3.3 (pip install asyncio), nginx 1.4.7

Parameters: ulimit -n 10240; nginx worker connections set to 10240

Start the web server with a single worker process:

# ../sbin/nginx

# ps ax | grep nginx

2007 ? Ss 0:00 nginx: master process ../sbin/nginx

2008 ? S 0:00 nginx: worker process

Run the benchmark tool, creating 10 k connections to nginx’s default test page:

$ python asyncli.py http://10.211.55.8/ -c 10000

nginx log shows an average of 548 requests per second.

Top output (sample):

VIRT RES SHR S %CPU %MEM TIME+ COMMAND

657m 115m 3860 R 60.2 6.2 4:30.02 python

54208 10m 848 R 7.0 0.6 0:30.79 nginx

Conclusion

Python implementation is concise—under 80 lines using only the standard library—illustrating Python’s readability compared to a C/C++ counterpart.

Python’s runtime efficiency is lower; after connections are established, Python consumes roughly ten times the CPU and RAM of nginx, indicating a two‑order‑of‑magnitude performance gap, which reflects the trade‑off between development speed and execution speed.

Single‑threaded asynchronous I/O versus multi‑threaded synchronous I/O: the demo shows async I/O is far more efficient; a 10 k thread model would require >600 MB of stack memory plus heavy context‑switch overhead.

asyncio Core Concepts

The following four concepts are essential when learning asyncio:

Event loop : the single‑threaded loop that drives asynchronous execution.

Future : represents the result of an asynchronous operation.

Coroutine : the function that defines the actual asynchronous task logic.

Generator (yield & yield from) : heavily used in asyncio for delegating execution.

Reference materials:

asyncio – Asynchronous I/O, event loop, coroutines and tasks, https://docs.python.org/3/library/asyncio.html

PEP 3156, Asynchronous IO Support Rebooted: the "asyncio" Module, http://legacy.python.org/dev/peps/pep-3156/

PEP 380, Syntax for Delegating to a Subgenerator, http://legacy.python.org/dev/peps/pep-0380/

PEP 342, Coroutines via Enhanced Generators, http://legacy.python.org/dev/peps/pep-0342/

PEP 255, Simple Generators, http://legacy.python.org/dev/peps/pep-0255/

asyncio source code, http://hg.python.org/cpython/file/3.4/Lib/asyncio/

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythonHTTPBenchmarkasynchronous I/Oasyncio
MaGe Linux Operations
Written by

MaGe Linux Operations

Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.