├── .gitignore ├── README.md ├── run_aiohttp.py ├── run_japronto.py ├── run_sanic.py └── shared.py /.gitignore: -------------------------------------------------------------------------------- 1 | env/ 2 | *.py[cod] 3 | .idea/ 4 | access.log 5 | error.log 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Requirements 2 | 3 | python 3.6, postgres, `pip install -U aiohttp uvloop ujson asyncpg sanic japronto` 4 | 5 | # Benchmarks 6 | 7 | ## aiohttp 8 | 9 | version: `3.4.0` 10 | 11 | ``` 12 | ➤ wrk -d 60 -c 2000 -t 12 --timeout 8 http://localhost:8000 # aiohttp 13 | Running 1m test @ http://localhost:8000 14 | 12 threads and 2000 connections 15 | Thread Stats Avg Stdev Max +/- Stdev 16 | Latency 72.02ms 20.86ms 1.01s 97.21% 17 | Req/Sec 1.16k 653.88 3.83k 63.73% 18 | 829953 requests in 1.00m, 132.18MB read 19 | Socket errors: connect 983, read 6443, write 0, timeout 0 20 | Requests/sec: 13820.94 21 | Transfer/sec: 2.20MB 22 | 23 | ➤ wrk -d 60 -c 2000 -t 12 --timeout 8 http://localhost:8000/db # aiohttp 24 | Running 1m test @ http://localhost:8000/db 25 | 12 threads and 2000 connections 26 | Thread Stats Avg Stdev Max +/- Stdev 27 | Latency 571.84ms 1.11s 7.99s 87.45% 28 | Req/Sec 237.39 125.57 1.16k 69.98% 29 | 170071 requests in 1.00m, 31.05MB read 30 | Socket errors: connect 983, read 5582220, write 0, timeout 283 31 | Requests/sec: 2831.93 32 | Transfer/sec: 529.47KB 33 | ``` 34 | 35 | ## sanic 36 | 37 | version: `0.7.0` 38 | 39 | ``` 40 | ➤ wrk -d 60 -c 2000 -t 12 --timeout 8 http://localhost:8000 # sanic 41 | Running 1m test @ http://localhost:8000 42 | 12 threads and 2000 connections 43 | Thread Stats Avg Stdev Max +/- Stdev 44 | Latency 34.35ms 28.43ms 1.74s 99.81% 45 | Req/Sec 2.48k 849.25 5.86k 57.85% 46 | 1776795 requests in 1.00m, 228.76MB read 47 | Socket errors: connect 983, read 13636, write 0, timeout 0 48 | Requests/sec: 29583.17 49 | Transfer/sec: 3.81MB 50 | 51 | ➤ wrk -d 60 -c 2000 -t 12 --timeout 8 http://localhost:8000/db # sanic 52 | Running 1m test @ http://localhost:8000/db 53 | 12 threads and 2000 connections 54 | Thread Stats Avg Stdev Max +/- Stdev 55 | Latency 1.16s 1.86s 8.00s 83.82% 56 | Req/Sec 71.41 166.42 1.40k 94.10% 57 | 25076 requests in 1.00m, 3.82MB read 58 | Socket errors: connect 983, read 12316451, write 0, timeout 1876 59 | Requests/sec: 417.31 60 | Transfer/sec: 65.05KB 61 | ``` 62 | 63 | ## japronto 64 | 65 | version: `japronto==0.1.1` 66 | 67 | Doesn't seem to work with my setup, I get a lot of 68 | `SystemError: NULL result without error in PyObject_Call` and no results from wrk. 69 | 70 | 71 | # Versions Details 72 | 73 | updated on 28th August 2018 74 | 75 | ``` 76 | ➤ python -V 77 | Python 3.6.5 78 | ``` 79 | 80 | ``` 81 | ➤ pip freeze 82 | aiofiles==0.4.0 83 | aiohttp==3.4.0 84 | async-timeout==3.0.0 85 | asyncpg==0.17.0 86 | attrs==18.1.0 87 | chardet==3.0.4 88 | httptools==0.0.11 89 | idna==2.7 90 | idna-ssl==1.1.0 91 | japronto==0.1.1 92 | multidict==4.3.1 93 | pkg-resources==0.0.0 94 | sanic==0.7.0 95 | ujson==1.35 96 | uvloop==0.11.2 97 | websockets==6.0 98 | yarl==1.2.6 99 | ``` 100 | 101 | ``` 102 | ➤ psql -U postgres -h localhost -c 'SELECT version()' 103 | PostgreSQL 10.5 (Ubuntu 10.5-0ubuntu0.18.04) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0, 64-bit 104 | ``` 105 | 106 | ``` 107 | ➤ wrk --version 108 | wrk 4.0.2 [epoll] Copyright (C) 2012 Will Glozer 109 | ``` 110 | -------------------------------------------------------------------------------- /run_aiohttp.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import random 3 | 4 | import ujson 5 | import uvloop 6 | from aiohttp import web 7 | from shared import setup_db 8 | 9 | 10 | async def startup(app): 11 | app['db'] = await setup_db(app.loop) 12 | 13 | 14 | async def cleanup(app): 15 | await app['db'].close() 16 | 17 | 18 | async def index(request): 19 | data = {'message': 'hello world'} 20 | body = ujson.dumps(data) 21 | return web.Response(body=body.encode(), content_type='application/json') 22 | 23 | 24 | async def db(request): 25 | user_id = random.randrange(1, 1000) 26 | async with request.app['db'].acquire() as conn: 27 | username = await conn.fetchval('SELECT name from users WHERE id=$1', user_id) 28 | data = {'message': f'hello {username}'} 29 | body = ujson.dumps(data) 30 | return web.Response(body=body.encode(), content_type='application/json') 31 | 32 | 33 | asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) 34 | app = web.Application() 35 | app.on_startup.append(startup) 36 | app.on_cleanup.append(cleanup) 37 | app.router.add_get('/', index) 38 | app.router.add_get('/db', db) 39 | 40 | web.run_app(app, port=8000, access_log=None) 41 | -------------------------------------------------------------------------------- /run_japronto.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | import ujson 4 | from japronto import Application 5 | from shared import setup_db 6 | 7 | 8 | def index(request): 9 | data = {'message': 'hello world'} 10 | body = ujson.dumps(data) 11 | return request.Response(text=body) 12 | 13 | 14 | async def db(request): 15 | user_id = random.randrange(1, 1000) 16 | async with request.app.db.acquire() as conn: 17 | username = await conn.fetchval('SELECT name from users WHERE id=$1', user_id) 18 | data = {'message': f'hello {username}'} 19 | body = ujson.dumps(data) 20 | return request.Response(text=body, headers={'Content-Type': 'application/json'}) 21 | 22 | 23 | app = Application() 24 | app.db = app.loop.run_until_complete(setup_db(app.loop)) 25 | app.router.add_route('/', index) 26 | app.router.add_route('/db', db) 27 | app.run(port=8000) # worker_num=2 works for index but not db 28 | -------------------------------------------------------------------------------- /run_sanic.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from sanic import Sanic 4 | from sanic.response import json 5 | from shared import setup_db 6 | 7 | app = Sanic() 8 | 9 | 10 | @app.listener('before_server_start') 11 | async def register_db(app, loop): 12 | app.db = await setup_db(loop) 13 | 14 | 15 | @app.listener('after_server_stop') 16 | async def cleanup(app, loop): 17 | await app.db.close() 18 | 19 | 20 | @app.route('/') 21 | async def index(request): 22 | data = {'message': 'hello world'} 23 | return json(data) 24 | 25 | 26 | @app.route('/db') 27 | async def get_form_db(request): 28 | user_id = random.randrange(1, 1000) 29 | async with request.app.db.acquire() as conn: 30 | username = await conn.fetchval('SELECT name from users WHERE id=$1', user_id) 31 | data = {'message': f'hello {username}'} 32 | return json(data) 33 | 34 | 35 | if __name__ == '__main__': 36 | app.run(host='localhost', port=8000, access_log=False) 37 | -------------------------------------------------------------------------------- /shared.py: -------------------------------------------------------------------------------- 1 | import random 2 | import string 3 | 4 | import asyncpg 5 | 6 | 7 | PG_DSN_NO_DB = 'postgres://postgres:waffle@localhost:5432' 8 | DB_NAME = 'foobar' 9 | PG_DSN = PG_DSN_NO_DB + '/' + DB_NAME 10 | 11 | DB_EXISTS = 'SELECT EXISTS (SELECT datname FROM pg_catalog.pg_database WHERE datname=$1)' 12 | 13 | CREATE_TABLES = """\ 14 | DROP SCHEMA public CASCADE; 15 | CREATE SCHEMA public; 16 | 17 | CREATE TABLE users ( 18 | id SERIAL PRIMARY KEY, 19 | name VARCHAR(63) NOT NULL 20 | ); 21 | """ 22 | 23 | 24 | async def setup_db(loop): 25 | conn = await asyncpg.connect(dsn=PG_DSN_NO_DB) 26 | try: 27 | db_exists = await conn.fetchval(DB_EXISTS, DB_NAME) 28 | if not db_exists: 29 | await conn.execute('CREATE DATABASE {}'.format(DB_NAME)) 30 | finally: 31 | await conn.close() 32 | 33 | conn = await asyncpg.connect(dsn=PG_DSN) 34 | try: 35 | await conn.execute(CREATE_TABLES) 36 | names = [ 37 | [''.join(random.choices(string.ascii_letters, k=random.randrange(10, 50)))] 38 | for _ in range(1000) 39 | ] 40 | 41 | await conn.executemany('INSERT INTO users (name) VALUES ($1);', names) 42 | finally: 43 | await conn.close() 44 | return await asyncpg.create_pool(PG_DSN, loop=loop, max_size=100) 45 | --------------------------------------------------------------------------------