これは、なにをしたくて書いたもの?
- Node.jsを使ったプログラミングの練習がてら、データストアにアクセスでも
- Redisあたりにアクセスしてみて、試してみようかと
- Promise使おう
A Node.js for Redis Client
Node.jsからRedisにアクセスするには、こちらのライブラリを使うようです。
GitHub - NodeRedis/node_redis: redis client for node
このライブラリは、コールバック形式が標準のようですが、Promiseを使いたければ別のライブラリの手を借りましょうと。
ドキュメントにも記載のあるとおり、今回はBluebirdを使うことにします。
環境
今回の環境は、こちら。
$ node -v v8.11.4 $ npm -v 5.6.0
Redisは、4.0.11を使います。リモートアクセスにするので、パスワード指定でアクセスする感じで。
準備
ライブラリのインストール。
$ npm i --save redis bluebird
"dependencies": { "bluebird": "^3.5.2", "redis": "^2.8.0" }
動作確認は、テストコードで行うことにしましょう。
Jestをインストール。
$ npm i --save-dev jest
"devDependencies": { "jest": "^23.5.0" }, "jest": { "testEnvironment": "node" }
scriptの指定は、こんな感じで。
"scripts": { "test": "jest" },
テストコードの雛形
テストコードの雛形は、こんな感じで。 test/redis.test.js
const Promise = require('bluebird'); const redis = Promise.promisifyAll(require('redis')); // ここに、テストを書く!!
Node.jsのRedisクライアントに対して、Bluebirdでラップします。
set/get
まずは単純に、set/getから。
test('simple put and get', async () => { const client = await redis.createClient(6379, '172.17.0.2', { password: 'redispass' }); await client.setAsync('key1', 'value1'); expect(await client.getAsync('key1')).toEqual('value1'); await client.delAsync('key1'); expect(await client.getAsync('key1')).toBeNull(); await client.setAsync('key2', 'value2'); expect(await client.getAsync('key2')).toEqual('value2'); await client.delAsync('key2', redis.print); expect(await client.getAsync('key2')).toBeNull(); await client.quitAsync(); });
Redisへの接続は、こんな感じで。
const client = await redis.createClient(6379, '172.17.0.2', { password: 'redispass' });
Redis#createClientのオプションについては、こちらを参照しましょう。
Redisの各コマンドは、Clientのfunctionとして表現されます。
で、今回はBluebirdを使ってPromiseとして扱っているので、こういう感じです、と。
await client.setAsync('key1', 'value1'); expect(await client.getAsync('key1')).toEqual('value1'); await client.delAsync('key1'); expect(await client.getAsync('key1')).toBeNull();
切断は、Client#quitです。
await client.quitAsync();
hmset/hgetall
ハッシュ的な。
test('hmset and get', async() => { const client = await redis.createClient(6379, '172.17.0.2', { password: 'redispass' }); await client.hmsetAsync('hkey1', { member1: 'value1-1', member2: 'value1-2' }); expect(await client.hgetallAsync('hkey1')).toEqual({ member1: 'value1-1', member2: 'value1-2' }); await client.delAsync('hkey1'); expect(await client.hgetallAsync('hkey1')).toBeNull(); await client.quitAsync(); });
オブジェクトを扱えるので、楽ですね。
Batch(Pipeline)
Pipelineはどうするのかな?と思っていたら、client#batchでやるみたいです。
test('use batch as pipeline', async () => { const client = await redis.createClient(6379, '172.17.0.2', { password: 'redispass' }); await client .batch() .set( 'key1', 'value1') .hmset('hkey1', { member1: 'value1-1', member2: 'value1-2' }) .execAsync(); const response = await client .batch() .get('key1') .hgetall('hkey1') .execAsync(); expect(response[0]).toEqual('value1'); expect(response[1]).toEqual({ member1: 'value1-1', member2: 'value1-2' }); await client .batch() .del('key1') .del('hkey1') .execAsync(); const deletedResponse = await client .batch() .get('key1') .hgetall('hkey1') .execAsync(); expect(deletedResponse[0]).toBeNull(); expect(deletedResponse[1]).toBeNull(); await client.quitAsync(); });
ただ、説明がほとんどありませんが…。
client#batchのあとに、各種コマンドを続けていき、最後にexecすればよい、と。
await client .batch() .set( 'key1', 'value1') .hmset('hkey1', { member1: 'value1-1', member2: 'value1-2' }) .execAsync();
multi
最後、multiです。
batchとほとんど一緒。
test('multi', async() => { const client = await redis.createClient(6379, '172.17.0.2', { password: 'redispass' }); await client .multi() .set( 'key1', 'value1') .hmset('hkey1', { member1: 'value1-1', member2: 'value1-2' }) .execAsync(); const response = await client .multi() .get('key1') .hgetall('hkey1') .execAsync(); expect(response[0]).toEqual('value1'); expect(response[1]).toEqual({ member1: 'value1-1', member2: 'value1-2' }); await client .multi() .del('key1') .del('hkey1') .execAsync(); const deletedResponse = await client .multi() .get('key1') .hgetall('hkey1') .execAsync(); expect(deletedResponse[0]).toBeNull(); expect(deletedResponse[1]).toBeNull(); client .multi() .set( 'key2', 'value2') .hmset('hkey2', { member1: 'value2-1', member2: 'value2-2' }) .discard(); // no async const discardedResponse = await client .multi() .get('key2') .hgetall('hkey2') .execAsync(); expect(discardedResponse[0]).toBeNull(); expect(discardedResponse[1]).toBeNull(); await client.quitAsync(); });
違いは、Client#multiで始めることくらいですね。
await client .multi() .set( 'key1', 'value1') .hmset('hkey1', { member1: 'value1-1', member2: 'value1-2' }) .execAsync();
discardもできるのですが、batchも同じことができそうな様子なのですが…。
client .multi() .set( 'key2', 'value2') .hmset('hkey2', { member1: 'value2-1', member2: 'value2-2' }) .discard(); // no async
とりあえず、基礎的なことや試せたのではないかなぁと思います。