aioredisを使って見る

# aioredis - asyncio (PEP 3156) Redis support

aioredisはasyncioを使ってredisとのやりとりを行うためのパッケージです。
redisがボトルネックになるような環境ではもしかしたら使えるかも?と思ったので試してみます。

Python3が対象です。

PyPI: https://pypi.python.org/pypi/aioredis
Document: http://aioredis.readthedocs.org/en/latest/

## インストール

いつものアレです。

```
$ pip install aioredis
DEPRECATION: --download-cache has been deprecated and will be removed in the future. Pip now automatically uses and configures its cache.
Collecting aioredis
  Downloading aioredis-0.2.2-py3-none-any.whl
Collecting hiredis (from aioredis)
  Downloading hiredis-0.2.0.tar.gz (46kB)
    100% |################################| 49kB 3.7MB/s
Installing collected packages: hiredis, aioredis
  Running setup.py install for hiredis
    building 'hiredis_for_hiredis_py' library
    clang -Wno-unused-result -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -c vendor/hiredis/read.c -o build/temp.macosx-10.10-x86_64-3.4/vendor/hiredis/read.o
    clang -Wno-unused-result -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -c vendor/hiredis/sds.c -o build/temp.macosx-10.10-x86_64-3.4/vendor/hiredis/sds.o
    ar rc build/temp.macosx-10.10-x86_64-3.4/libhiredis_for_hiredis_py.a build/temp.macosx-10.10-x86_64-3.4/vendor/hiredis/read.o build/temp.macosx-10.10-x86_64-3.4/vendor/hiredis/sds.o
    ranlib build/temp.macosx-10.10-x86_64-3.4/libhiredis_for_hiredis_py.a
    building 'hiredis.hiredis' extension

〜省略〜

Successfully installed aioredis-0.2.2 hiredis-0.2.0

```

特に何も問題なくすんなり入りました。

# サンプルコードを試してみる

ではサンプルコードを試してみます。
redis-serverを起動しておいてくださいね。

## Simple low-level interface

READMEのものをそのままコピペです。

```py3:simple.py

import asyncio
import aioredis

loop = asyncio.get_event_loop()

@asyncio.coroutine
def go():
    conn = yield from aioredis.create_connection(
        ('localhost', 6379), loop=loop)
    yield from conn.execute('set', 'my-key', 'value')
    val = yield from conn.execute('get', 'my-key')
    print(val)
    conn.close()
loop.run_until_complete(go())
```

aioredis.create_connection()でコネクションオブジェクトを生成しています。
実行してみます。

```shell-session:実行結果
$ python simple.py 
b'value'
```

`redis-cli monitor` で見ているとちゃんと設定されていることがわかります。

```shell-session:monitor
1437429410.490170 [0 [::1]:60100] "SET" "my-key" "value"
1437429410.490412 [0 [::1]:60100] "GET" "my-key"
```

## Simple high-level interface:

こちらもREADMEのものをそのままコピペです。

```py3:high-level.py
import asyncio
import aioredis

loop = asyncio.get_event_loop()

@asyncio.coroutine
def go():
    redis = yield from aioredis.create_redis(
        ('localhost', 6379), loop=loop)
    yield from redis.set('my-key', 'value')
    val = yield from redis.get('my-key')
    print(val)
    redis.close()
loop.run_until_complete(go())
# will print 'value'
```

違いはlow-levelのものがaioredis.create_connection()でコネクションオブジェクトを生成していたのに対し、high-levelではaioredis.create_redis()でredisのコマンドのwrapper関数を実装したオブジェクトを生成しています。
そのため、execute()で実行するのではなく、get()メソッドとset()メソッドで値の保存と取得をしています。
では実行してみます。

```shell-session:実行結果
$ python high-level.py 
b'value'
```

```shell-session:monitor
1437429812.921592 [0 [::1]:60140] "SET" "my-key" "value"
1437429812.921852 [0 [::1]:60140] "GET" "my-key"
```

先ほどと同じ結果が得られました。
redis-cli monitorも同様に出力されています。

## コネクションプール

コネクションプールも面倒見てくれそうです。
こちらもREADMEのコピペです。

```py3:connection-pool.py
import asyncio
import aioredis

loop = asyncio.get_event_loop()

@asyncio.coroutine
def go():
    pool = yield from aioredis.create_pool(
        ('localhost', 6379),
        minsize=5, maxsize=10,
        loop=loop)
    with (yield from pool) as redis:    # high-level redis API instance
        yield from redis.set('my-key', 'value')
        print((yield from redis.get('my-key')))
    pool.clear()    # closing all open connections

loop.run_until_complete(go())
```

ここでは aioredis.create_pool()でコネクションプールの管理オブジェクトを作成し、
それにevent_loopを渡しています。

では実行してみます。

```shell-session:実行結果
$ python connection-pool.py
b'value'
Task was destroyed but it is pending!
task: <Task pending coro=<_read_data() running at /Users/sximada/ng/var/lib/virtualenv/py3k/lib/python3.4/site-packages/aioredis/connection.py:104> wait_for=<Future pending cb=[Task._wakeup()]> cb=[Future.set_result()]>
Task was destroyed but it is pending!
task: <Task pending coro=<_read_data() running at /Users/sximada/ng/var/lib/virtualenv/py3k/lib/python3.4/site-packages/aioredis/connection.py:104> wait_for=<Future pending cb=[Task._wakeup()]> cb=[Future.set_result()]>
Task was destroyed but it is pending!
task: <Task pending coro=<_read_data() running at /Users/sximada/ng/var/lib/virtualenv/py3k/lib/python3.4/site-packages/aioredis/connection.py:104> wait_for=<Future pending cb=[Task._wakeup()]> cb=[Future.set_result()]>
Task was destroyed but it is pending!
task: <Task pending coro=<_read_data() running at /Users/sximada/ng/var/lib/virtualenv/py3k/lib/python3.4/site-packages/aioredis/connection.py:104> wait_for=<Future pending cb=[Task._wakeup()]> cb=[Future.set_result()]>
Task was destroyed but it is pending!
task: <Task pending coro=<_read_data() running at /Users/sximada/ng/var/lib/virtualenv/py3k/lib/python3.4/site-packages/aioredis/connection.py:104> wait_for=<Future pending cb=[Task._wakeup()]> cb=[Future.set_result()]>
(py3k)
```

```shell-session:monitor
1437430657.702979 [0 [::1]:60332] "SELECT" "0"
1437430657.704267 [0 [::1]:60333] "SELECT" "0"
1437430657.705696 [0 [::1]:60334] "SELECT" "0"
1437430657.706967 [0 [::1]:60335] "SELECT" "0"
1437430657.708267 [0 [::1]:60336] "SELECT" "0"
1437430657.708580 [0 [::1]:60332] "SET" "my-key" "value"
1437430657.708821 [0 [::1]:60332] "GET" "my-key"
```