{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import os\n", "import signal\n", "import gunicorn.app.base\n", "from multiprocessing import Process\n", "from contextlib import contextmanager\n", "\n", "HOST, PORT = \"127.0.0.1\", \"8080\"\n", "\n", "class BenchmarkApplication(gunicorn.app.base.BaseApplication):\n", "\n", " def __init__(self, app, worker_class, threads):\n", " self.worker_class = worker_class\n", " self.threads = threads\n", " self.application = app\n", " super().__init__()\n", "\n", " def load_config(self):\n", " self.cfg.set(\"bind\", f\"{HOST}:{PORT}\")\n", " self.cfg.set(\"workers\", 2)\n", " self.cfg.set(\"worker_class\", self.worker_class)\n", " self.cfg.set(\"threads\", self.threads)\n", " \n", " def load(self):\n", " return self.application\n", "\n", "def run(app_func, worker, threads):\n", " app = app_func()\n", " BenchmarkApplication(app, worker, threads).run()\n", " \n", "@contextmanager\n", "def app_running(app_func, worker, threads=1):\n", " p = Process(target=lambda: run(app_func, worker, threads))\n", " p.start()\n", " time.sleep(1)\n", " print(\"allowed sleep\")\n", " yield \n", " os.kill(p.pid, signal.SIGINT)\n", " print(\"kill sent\")\n", " p.join(timeout=5)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": true }, "outputs": [], "source": [ "%matplotlib inline\n", "import time\n", "from multiprocessing import Process\n", "\n", "class Stats:\n", " def __init__(self):\n", " self.request_latencies = []\n", " self.timed_out = 0\n", " self.elapsed_time = 0\n", "\n", "async def fetch(stats, session, url):\n", " start_time = time.time()\n", " try:\n", " async with session.get(url) as response:\n", " await response.text()\n", " except:\n", " stats.timed_out += 1\n", " else:\n", " elapsed_time = time.time() - start_time\n", " stats.request_latencies.append(elapsed_time)\n", "\n", "import aiohttp\n", "TOTAL_COUNT = 5000\n", "MAX_CONNECTIONS = 200\n", "\n", "async def benchmark(root=\"http://localhost:8080/\"):\n", " url = root + \"foo\"\n", " stats = Stats()\n", " start_time = time.time()\n", " conn = aiohttp.TCPConnector(limit=MAX_CONNECTIONS)\n", " async with aiohttp.ClientSession(connector=conn) as session:\n", " task_list = []\n", " for i in range(TOTAL_COUNT):\n", " task_list.append(fetch(stats, session, url))\n", " await asyncio.gather(*task_list)\n", " elapsed_time = time.time() - start_time\n", " stats.elapsed_time = elapsed_time\n", " return stats" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "[2017-07-05 21:41:30 -0700] [7641] [INFO] Starting gunicorn 19.7.1\n", "[2017-07-05 21:41:30 -0700] [7641] [INFO] Listening at: http://127.0.0.1:8080 (7641)\n", "[2017-07-05 21:41:30 -0700] [7641] [INFO] Using worker: aiohttp.worker.GunicornWebWorker\n", "[2017-07-05 21:41:30 -0700] [7642] [INFO] Booting worker with pid: 7642\n", "[2017-07-05 21:41:30 -0700] [7643] [INFO] Booting worker with pid: 7643\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "allowed sleep\n" ] } ], "source": [ "from aiohttp import web\n", "import asyncio\n", "\n", "def create_aiohttp_app():\n", " loop = asyncio.new_event_loop()\n", " asyncio.set_event_loop(loop)\n", "\n", " async def handle(request):\n", " name = request.match_info.get('name', \"Anonymous\")\n", " await asyncio.sleep(0.1)\n", " text = \"Hello, \" + name\n", " return web.Response(text=text)\n", "\n", " aiohttp_app = web.Application()\n", " aiohttp_app.router.add_get('/', handle)\n", " aiohttp_app.router.add_get('/{name}', handle)\n", " return aiohttp_app\n", " \n", "import asyncio\n", "loop = asyncio.get_event_loop()\n", "with app_running(create_aiohttp_app, \"aiohttp.worker.GunicornWebWorker\"):\n", " stats = loop.run_until_complete(benchmark())\n", " \n", "import matplotlib.pyplot as plt\n", "\n", "print(stats.elapsed_time)\n", "plt.hist(stats.request_latencies, bins=100)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "[2017-07-05 21:40:45 -0700] [7625] [INFO] Starting gunicorn 19.7.1\n", "[2017-07-05 21:40:45 -0700] [7625] [INFO] Listening at: http://127.0.0.1:8080 (7625)\n", "[2017-07-05 21:40:45 -0700] [7625] [INFO] Using worker: threads\n", "[2017-07-05 21:40:45 -0700] [7626] [INFO] Booting worker with pid: 7626\n", "[2017-07-05 21:40:45 -0700] [7629] [INFO] Booting worker with pid: 7629\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "allowed sleep\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[2017-07-05 21:41:04 -0700] [7625] [INFO] Handling signal: int\n", "[2017-07-05 21:41:04 -0700] [7626] [INFO] Worker exiting (pid: 7626)\n", "[2017-07-05 21:41:04 -0700] [7629] [INFO] Worker exiting (pid: 7629)\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "kill sent\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "[2017-07-05 21:41:04 -0700] [7625] [INFO] Shutting down: Master\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "18.032105922698975\n" ] }, { "data": { "text/plain": [ "(array([ 40., 40., 40., 23., 37., 40., 32., 28., 40., 40., 22.,\n", " 38., 36., 19., 22., 23., 17., 11., 11., 7., 8., 8.,\n", " 7., 5., 8., 7., 5., 8., 7., 6., 7., 8., 5.,\n", " 7., 8., 6., 9., 5., 5., 5., 4., 6., 5., 4.,\n", " 6., 5., 4., 6., 4., 5., 6., 4., 5., 6., 4.,\n", " 5., 6., 5., 5., 5., 4., 6., 5., 4., 6., 5.,\n", " 4., 6., 5., 4., 6., 5., 5., 5., 5., 5., 5.,\n", " 5., 5., 5., 5., 5., 5., 5., 5., 5., 5., 5.,\n", " 5., 5., 5., 5., 5., 4., 6., 5., 5., 5., 5.,\n", " 5.]), array([ 0.42265677, 0.59651399, 0.7703712 , 0.94422842,\n", " 1.11808563, 1.29194285, 1.46580006, 1.63965728,\n", " 1.81351449, 1.9873717 , 2.16122892, 2.33508613,\n", " 2.50894335, 2.68280056, 2.85665778, 3.03051499,\n", " 3.20437221, 3.37822942, 3.55208663, 3.72594385,\n", " 3.89980106, 4.07365828, 4.24751549, 4.42137271,\n", " 4.59522992, 4.76908714, 4.94294435, 5.11680156,\n", " 5.29065878, 5.46451599, 5.63837321, 5.81223042,\n", " 5.98608764, 6.15994485, 6.33380207, 6.50765928,\n", " 6.68151649, 6.85537371, 7.02923092, 7.20308814,\n", " 7.37694535, 7.55080257, 7.72465978, 7.898517 ,\n", " 8.07237421, 8.24623142, 8.42008864, 8.59394585,\n", " 8.76780307, 8.94166028, 9.1155175 , 9.28937471,\n", " 9.46323193, 9.63708914, 9.81094635, 9.98480357,\n", " 10.15866078, 10.332518 , 10.50637521, 10.68023243,\n", " 10.85408964, 11.02794686, 11.20180407, 11.37566128,\n", " 11.5495185 , 11.72337571, 11.89723293, 12.07109014,\n", " 12.24494736, 12.41880457, 12.59266179, 12.766519 ,\n", " 12.94037621, 13.11423343, 13.28809064, 13.46194786,\n", " 13.63580507, 13.80966229, 13.9835195 , 14.15737672,\n", " 14.33123393, 14.50509115, 14.67894836, 14.85280557,\n", " 15.02666279, 15.20052 , 15.37437722, 15.54823443,\n", " 15.72209165, 15.89594886, 16.06980608, 16.24366329,\n", " 16.4175205 , 16.59137772, 16.76523493, 16.93909215,\n", " 17.11294936, 17.28680658, 17.46066379, 17.63452101, 17.80837822]), )" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\nGDu3lfYFoiAAAAAASUVORK5CYII=\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from flask import Flask\n", "flask_app = Flask(__name__)\n", "import time\n", "\n", "@flask_app.route(\"/\")\n", "def hello(name):\n", " time.sleep(0.1)\n", " return \"Hello World!\"\n", "\n", "import asyncio\n", "loop = asyncio.get_event_loop()\n", "with app_running(lambda: flask_app, \"sync\", threads=10):\n", " stats = loop.run_until_complete(benchmark())\n", " \n", "import matplotlib.pyplot as plt\n", "\n", "print(stats.elapsed_time)\n", "plt.hist(stats.request_latencies, bins=100)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.1" } }, "nbformat": 4, "nbformat_minor": 2 }