Browse Source

Add MySQL support for aiohttp.web

Ludovic Gasc (GMLudo) 9 years ago
parent
commit
165a7c58ec

+ 8 - 0
frameworks/Python/asyncio/aiohttp.web/etc/hello/main/main.yaml

@@ -8,6 +8,14 @@ engines:
     password: benchmarkdbpass
     password: benchmarkdbpass
     minsize: 22
     minsize: 22
     maxsize: 22
     maxsize: 22
+  mysql:
+    host: 127.0.0.1
+    port: 3306
+    db: hello_world
+    user: benchmarkdbuser
+    pwd: benchmarkdbpass
+    minsize: 22
+    maxsize: 22
   redis:
   redis:
     host: 127.0.0.1
     host: 127.0.0.1
     port: 6379
     port: 6379

+ 21 - 0
frameworks/Python/asyncio/aiohttp.web/hello/__init__.py

@@ -9,6 +9,7 @@ import asyncio_redis
 from asyncio_redis.protocol import HiRedisProtocol
 from asyncio_redis.protocol import HiRedisProtocol
 import aiohttp.web
 import aiohttp.web
 import aiohttp_jinja2
 import aiohttp_jinja2
+import aiomysql
 import api_hour
 import api_hour
 
 
 from . import endpoints
 from . import endpoints
@@ -27,12 +28,16 @@ class Container(api_hour.Container):
         self.servers['http'].router.add_route('GET', '/json', endpoints.world.json)
         self.servers['http'].router.add_route('GET', '/json', endpoints.world.json)
         self.servers['http'].router.add_route('GET', '/db', endpoints.world.db)
         self.servers['http'].router.add_route('GET', '/db', endpoints.world.db)
         self.servers['http'].router.add_route('GET', '/db_redis', endpoints.world.db_redis)
         self.servers['http'].router.add_route('GET', '/db_redis', endpoints.world.db_redis)
+        self.servers['http'].router.add_route('GET', '/db_mysql', endpoints.world.db_mysql)
         self.servers['http'].router.add_route('GET', '/queries', endpoints.world.queries)
         self.servers['http'].router.add_route('GET', '/queries', endpoints.world.queries)
         self.servers['http'].router.add_route('GET', '/queries_redis', endpoints.world.queries_redis)
         self.servers['http'].router.add_route('GET', '/queries_redis', endpoints.world.queries_redis)
+        self.servers['http'].router.add_route('GET', '/queries_mysql', endpoints.world.queries_mysql)
         self.servers['http'].router.add_route('GET', '/fortunes', endpoints.world.fortunes)
         self.servers['http'].router.add_route('GET', '/fortunes', endpoints.world.fortunes)
         self.servers['http'].router.add_route('GET', '/fortunes_redis', endpoints.world.fortunes_redis)
         self.servers['http'].router.add_route('GET', '/fortunes_redis', endpoints.world.fortunes_redis)
+        self.servers['http'].router.add_route('GET', '/fortunes_mysql', endpoints.world.fortunes_mysql)
         self.servers['http'].router.add_route('GET', '/updates', endpoints.world.updates)
         self.servers['http'].router.add_route('GET', '/updates', endpoints.world.updates)
         self.servers['http'].router.add_route('GET', '/updates_redis', endpoints.world.updates_redis)
         self.servers['http'].router.add_route('GET', '/updates_redis', endpoints.world.updates_redis)
+        self.servers['http'].router.add_route('GET', '/updates_mysql', endpoints.world.updates_mysql)
         self.servers['http'].router.add_route('GET', '/plaintext', endpoints.world.plaintext)
         self.servers['http'].router.add_route('GET', '/plaintext', endpoints.world.plaintext)
 
 
     def make_servers(self):
     def make_servers(self):
@@ -56,6 +61,16 @@ class Container(api_hour.Container):
                                                                      minsize=int(self.config['engines']['pg']['minsize']),
                                                                      minsize=int(self.config['engines']['pg']['minsize']),
                                                                      maxsize=int(self.config['engines']['pg']['maxsize']),
                                                                      maxsize=int(self.config['engines']['pg']['maxsize']),
                                                                      loop=self.loop))
                                                                      loop=self.loop))
+        self.engines['mysql'] = self.loop.create_task(aiomysql.create_pool(
+                host=self.config['engines']['mysql']['host'],
+                port=self.config['engines']['mysql']['port'],
+                user=self.config['engines']['mysql']['user'],
+                password=self.config['engines']['mysql']['pwd'],
+                db=self.config['engines']['mysql']['db'],
+                minsize=int(self.config['engines']['mysql']['minsize']),
+                maxsize=int(self.config['engines']['mysql']['maxsize']),
+                cursorclass=aiomysql.DictCursor,
+                loop=self.loop))
         yield from asyncio.wait([self.engines['pg']], return_when=asyncio.ALL_COMPLETED)
         yield from asyncio.wait([self.engines['pg']], return_when=asyncio.ALL_COMPLETED)
         self.engines['redis'] = yield from asyncio_redis.Pool.create(host=self.config['engines']['redis']['host'],
         self.engines['redis'] = yield from asyncio_redis.Pool.create(host=self.config['engines']['redis']['host'],
                                                                      port=self.config['engines']['redis']['port'],
                                                                      port=self.config['engines']['redis']['port'],
@@ -74,6 +89,12 @@ class Container(api_hour.Container):
                 yield from self.engines['pg'].result().wait_closed()
                 yield from self.engines['pg'].result().wait_closed()
             else:
             else:
                 yield from self.engines['pg'].cancel()
                 yield from self.engines['pg'].cancel()
+        if 'mysql' in self.engines:
+            if self.engines['mysql'].done():
+                self.engines['mysql'].result().close()
+                yield from self.engines['mysql'].result().wait_closed()
+            else:
+                yield from self.engines['mysql'].cancel()
         if 'redis' in self.engines:
         if 'redis' in self.engines:
             self.engines['redis'].close()
             self.engines['redis'].close()
             yield from asyncio.sleep(1) # wait redis close connection
             yield from asyncio.sleep(1) # wait redis close connection

+ 33 - 1
frameworks/Python/asyncio/aiohttp.web/hello/endpoints/world.py

@@ -7,7 +7,7 @@ import aiohttp_jinja2
 
 
 from ..services import queries_number
 from ..services import queries_number
 from ..services.world import get_random_record, get_random_records, update_random_records, get_fortunes
 from ..services.world import get_random_record, get_random_records, update_random_records, get_fortunes
-from ..services import redis
+from ..services import redis, mysql
 
 
 LOG = logging.getLogger(__name__)
 LOG = logging.getLogger(__name__)
 
 
@@ -30,6 +30,13 @@ def db_redis(request):
 
 
     return JSON((yield from redis.get_random_record(container)))
     return JSON((yield from redis.get_random_record(container)))
 
 
[email protected]
+def db_mysql(request):
+    """Test type 2: Single database query"""
+    container = request.app.ah_container
+
+    return JSON((yield from mysql.get_random_record(container)))
+
 @asyncio.coroutine
 @asyncio.coroutine
 def queries(request):
 def queries(request):
     """Test type 3: Multiple database queries"""
     """Test type 3: Multiple database queries"""
@@ -46,6 +53,14 @@ def queries_redis(request):
 
 
     return JSON((yield from redis.get_random_records(container, limit)))
     return JSON((yield from redis.get_random_records(container, limit)))
 
 
[email protected]
+def queries_mysql(request):
+    """Test type 3: Multiple database queries"""
+    container = request.app.ah_container
+    limit = queries_number(request.GET.get('queries', 1))
+
+    return JSON((yield from mysql.get_random_records(container, limit)))
+
 @asyncio.coroutine
 @asyncio.coroutine
 def fortunes(request):
 def fortunes(request):
     """Test type 4: Fortunes"""
     """Test type 4: Fortunes"""
@@ -64,6 +79,15 @@ def fortunes_redis(request):
                                           request,
                                           request,
                                           {'fortunes': (yield from redis.get_fortunes(container))})
                                           {'fortunes': (yield from redis.get_fortunes(container))})
 
 
[email protected]
+def fortunes_mysql(request):
+    """Test type 4: Fortunes"""
+    container = request.app.ah_container
+
+    return aiohttp_jinja2.render_template('fortunes.html.j2',
+                                          request,
+                                          {'fortunes': (yield from mysql.get_fortunes(container))})
+
 @asyncio.coroutine
 @asyncio.coroutine
 def updates(request):
 def updates(request):
     """Test type 5: Database updates"""
     """Test type 5: Database updates"""
@@ -80,6 +104,14 @@ def updates_redis(request):
 
 
     return JSON((yield from redis.update_random_records(container, limit)))
     return JSON((yield from redis.update_random_records(container, limit)))
 
 
[email protected]
+def updates_mysql(request):
+    """Test type 5: Database updates"""
+    container = request.app.ah_container
+    limit = queries_number(request.GET.get('queries', 1))
+
+    return JSON((yield from mysql.update_random_records(container, limit)))
+
 @asyncio.coroutine
 @asyncio.coroutine
 def plaintext(request):
 def plaintext(request):
     """Test type 6: Plaintext"""
     """Test type 6: Plaintext"""

+ 56 - 0
frameworks/Python/asyncio/aiohttp.web/hello/services/mysql.py

@@ -0,0 +1,56 @@
+import asyncio
+from random import randint
+from operator import itemgetter
+
+
[email protected]
+def get_random_record(container):
+    with (yield from container.engines['mysql'].result()) as mysql_conn:
+        cur = yield from mysql_conn.cursor()
+        yield from cur.execute('SELECT id AS "Id", randomnumber AS "RandomNumber" FROM world WHERE id=%(idx)s LIMIT 1',
+                               {'idx': randint(1, 10000)})
+        world = yield from cur.fetchone()
+    return world
+
+
[email protected]
+def get_random_records(container, limit):
+    results = []
+    with (yield from container.engines['mysql'].result()) as mysql_conn:
+        cur = yield from mysql_conn.cursor()
+        for i in range(limit):
+            yield from cur.execute('SELECT id AS "Id", randomnumber AS "RandomNumber" FROM world WHERE id=%(idx)s LIMIT 1',
+                                   {'idx': randint(1, 10000)})
+            results.append((yield from cur.fetchone()))
+
+    return results
+
+
[email protected]
+def update_random_records(container, limit):
+    results = []
+
+    with (yield from container.engines['mysql'].result()) as mysql_conn:
+        cur = yield from mysql_conn.cursor()
+        for i in range(limit):
+            yield from cur.execute('SELECT id AS "Id", randomnumber AS "RandomNumber" FROM world WHERE id=%(idx)s LIMIT 1',
+                                   {'idx': randint(1, 10000)})
+            world = yield from cur.fetchone()
+            yield from cur.execute('UPDATE world SET randomnumber=%(random_number)s WHERE id=%(idx)s',
+                                   {'random_number': randint(1, 10000), 'idx': world['Id']})
+            results.append(world)
+    return results
+
+
[email protected]
+def get_fortunes(container):
+    with (yield from container.engines['mysql'].result()) as mysql_conn:
+        cur = yield from mysql_conn.cursor()
+        yield from cur.execute('SELECT * FROM fortune')
+        fortunes = yield from cur.fetchall()
+
+    fortunes.append({'id': 0, 'message': 'Additional fortune added at request time.'})
+
+    fortunes.sort(key=itemgetter('message'))
+
+    return fortunes

+ 20 - 0
frameworks/Python/asyncio/benchmark_config.json

@@ -43,6 +43,26 @@
       "display_name": "API-Hour+aiohttp.web+redis",
       "display_name": "API-Hour+aiohttp.web+redis",
       "notes": "Python 3 + API-Hour + AsyncIO + aiohttp.web + Redis"
       "notes": "Python 3 + API-Hour + AsyncIO + aiohttp.web + Redis"
     },
     },
+    "mysql": {
+      "setup_file": "aiohttp.web/setup",
+      "db_url": "/db_mysql",
+      "query_url": "/queries_mysql?queries=",
+      "fortune_url": "/fortunes_mysql",
+      "update_url": "/updates_mysql?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "MySQL",
+      "framework": "aiohttp.web",
+      "language": "Python",
+      "orm": "Raw",
+      "platform": "API-Hour",
+      "webserver": "Gunicorn",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "API-Hour+aiohttp.web+mysql",
+      "notes": "Python 3 + API-Hour + AsyncIO + aiohttp.web + MySQL"
+    },
     "json": {
     "json": {
       "setup_file": "yocto_http/setup",
       "setup_file": "yocto_http/setup",
       "json_url": "/json",
       "json_url": "/json",

+ 2 - 0
frameworks/Python/asyncio/requirements.txt

@@ -16,3 +16,5 @@ requests-futures==0.9.5
 setproctitle==1.1.8
 setproctitle==1.1.8
 six==1.9.0
 six==1.9.0
 ujson==1.33
 ujson==1.33
+aiomysql==0.0.7
+PyMySQL==0.6.7