Created
April 23, 2023 20:05
-
-
Save bsidhom/2e6eed44e57b5d707a8683a9e975c6bf to your computer and use it in GitHub Desktop.
Benchmark a single-threaded aiohttp server serving simple responses
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import argparse | |
import asyncio | |
import concurrent.futures | |
import functools | |
import time | |
import typing | |
import aiohttp | |
async def main(): | |
parser = argparse.ArgumentParser("Spam http requests and count qps") | |
parser.add_argument("--num-requests", | |
help="Number of requests to issue", | |
type=int, | |
required=True) | |
parser.add_argument( | |
"--parallelism", | |
help= | |
"Number of parallel clients to run. Each will send the target request count.", | |
type=int, | |
default=1) | |
parser.add_argument( | |
"--multiprocess", | |
help="If set, use multiple processes to send parallel requests.", | |
action="store_true") | |
args = parser.parse_args() | |
client = 0 | |
total_success_count = 0 | |
total_error_count = 0 | |
start_ns = time.perf_counter_ns() | |
if args.multiprocess: | |
with concurrent.futures.ProcessPoolExecutor() as pool: | |
loop = asyncio.get_running_loop() | |
tasks = [ | |
loop.run_in_executor( | |
pool, | |
functools.partial(run_test_blocking, args.num_requests)) | |
for _ in range(args.parallelism) | |
] | |
else: | |
tasks = [run_test(args.num_requests) for _ in range(args.parallelism)] | |
for task in asyncio.as_completed(tasks): | |
elapsed_sec, request_count, success_count = await task | |
print(f"client {client} results:") | |
print(f" issued {request_count} requests in {elapsed_sec} seconds") | |
success_rate = success_count / elapsed_sec | |
print(f" success rate: {success_rate} qps") | |
error_rate = (request_count - success_count) / elapsed_sec | |
print(f" error rate: {error_rate} qps") | |
total_success_count += success_count | |
total_error_count += request_count - success_count | |
client += 1 | |
elapsed_ns = time.perf_counter_ns() - start_ns | |
elapsed_sec = elapsed_ns / 1e9 | |
total_success_rate = total_success_count / elapsed_sec | |
total_error_rate = total_error_count / elapsed_sec | |
print(f"total success rate: {total_success_rate} qps") | |
print(f"total error rate: {total_error_rate} qps") | |
def run_test_blocking(requests: int) -> typing.Tuple[float, int, int]: | |
return asyncio.run(run_test(requests)) | |
async def run_test(requests: int) -> typing.Tuple[float, int, int]: | |
issued_requests = 0 | |
success_count = 0 | |
async with aiohttp.ClientSession() as session: | |
start = time.perf_counter_ns() | |
while issued_requests < requests: | |
issued_requests += 1 | |
async with session.get("http://localhost:8888/?name=foo") as resp: | |
if resp.ok: | |
t = await resp.text("utf-8") | |
if t == "Hello, foo!": | |
success_count += 1 | |
elapsed_ns = time.perf_counter_ns() - start | |
elapsed_sec = elapsed_ns / 1e9 | |
return (elapsed_sec, success_count, issued_requests) | |
if __name__ == "__main__": | |
asyncio.run(main()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import asyncio | |
import multidict | |
import yarl | |
from aiohttp import web | |
async def main(): | |
shutdown = asyncio.Event() | |
app = web.Application() | |
app.add_routes([web.get("/", hello)]) | |
runner = web.AppRunner(app) | |
await runner.setup() | |
site = web.TCPSite(runner, "localhost", 8888) | |
await site.start() | |
await shutdown.wait() | |
async def hello(request: web.Request) -> web.Response: | |
url: yarl.URL = request.rel_url | |
query: multidict.MultiDict = url.query | |
name = query.get("name", "mysterious") | |
return web.Response(text=f"Hello, {name}!") | |
if __name__ == "__main__": | |
asyncio.run(main()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment