DockerからRedisを立てて、ioredisから接続する

## Redisとは？

オープンソースのKey-Value型のデータベース。利用できるデータ構造は様々存在する。パフォーマンスの高速性からとても人気がある。

今回はDockerからこれを立てて、もう一つのコンテナから接続、操作をしてみる。

## まずは立ててみよう

公式イメージが用意されている。

https://hub.docker.com/_/redis

以下の```docker-compose.yml```からコンテナを作ってみる。

```yml:docker-compose.yml
version: '3.7'
services:
  redis:
    image: redis
    ports:
      - 6379:6379
```

これでコンテナを作成。
```
docker compose up
```



## ターミナルからデータを操作してみよう

作成したターミナルに接続してみる。
```
docker exec -it <作成したコンテナid> bash
```
```redis-cli``` で中を見てみる。
```
redis-cli
```

試しにデータの操作を行ってみよう。```set key value```で値を入れる。
```
127.0.0.1:6379> set test-key test-value
OK
```
```keys *```で全てのキーを取得、```set key value```で値を確認できる。

```
127.0.0.1:6379> keys *
1) "test-key"  
127.0.0.1:6379> get test-key
"test-value" 
```

```del key value```でキーの削除ができる。
```
127.0.0.1:6379> del test-key
(integer) 1  
127.0.0.1:6379> keys *
(empty array)
```

このように操作ができた。コマンドはこの他にも以下のようなもの↓がいろいろある。

https://redis.io/commands/?group=generic

## 別のコンテナから操作してみる

別のコンテナからredisに対しての接続を行い、```ioredis```を用いて操作してみる。

```docker-compose.yml```を修正する。

```yml:docker-compose.yml
version: '3.7'
services:
  redis:
    image: redis
    volumes:
      - ./redis/data:/data
    ports:
      - 6379:6379
  app:
    image: node
    volumes:
      - ./app:/app
    depends_on:
      - redis
    ports:
      - 3000:3000
    tty: true
    working_dir: "/app"      
    command: bash -c "npm run start"
```
* redisとappの二つのコンテナを作成する。
    * ```docker-compose.yml```を置いたディレクトリに、```app```ディレクトリと```redis```ディレクトリを作成する。
        * ```volumes:```でその中身をコピーする。
    * appへの接続はポート```3000```で行い、そこからredisへの接続はポート```6379```で行う。

```app```下で```npm init -y```を行う。ここに以下のファイル```index.ts```を作成する。

```ts:index.ts
import express from "express";
import Redis from "ioredis";
const app = express();
const port = 3000;

const redisPort = 6379;
const redis = new Redis(redisPort, "redis")

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.post("/posts", async(req, res) => {
    await redis.set(req.body.id, req.body.name);
    return res.send(req.body);
})

app.get("/gets", async(req, res) => {
    const name = await redis.get(<string>req.query.id);
    return res.send(name);
})

app.listen(port, () => console.log(`SERVER started on localhost:${port}`));
```

```posts```で受け取った情報を```redis.set(req.body.id, req.body.name)```で登録、
```gets```で指定されたidから```redis.get(<string>req.query.id)```で値を取得して返す。

```package.json```で以下のスクリプトをセットしておく。
```:package.json
  "scripts": {
    "start": "npx ts-node index.ts"
  }
```

コンテナ実行時に```bash -c "npm run start"```でこれが実行される。

```localhost:3000/posts```と```localhost:3000/gets```に対してPOST,リクエストを送信し、正しく取得できれば成功。

## ハッシュ型のデータ構造

別のデータ構造を試してみる。まず前のデータを消しておこう。

```
flushall
```

ハッシュ型のデータ構造を試してみる。

https://redis.io/commands/hset/

こちらでは```key```と```field```の二つから情報を登録することができる。RDBのような感覚で使うことができるようになる。

```
HSET key field value
HGET key field
```

index.tsを以下のように書き換える。

```ts:index.ts
import express from "express";
import Redis from "ioredis";
const app = express();
const port = 3000;

const redisPort = 6379;
const redis = new Redis(redisPort, "redis")


app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.post("/posts", async(req, res) => {
    await redis.hset(<string>req.body.id, <string>req.body.field, <string>req.body.value);
    return res.send(req.body);
})

app.get("/gets", async(req, res) => {
    const value = await redis.hget(<string>req.query.id, <string>req.query.field);
    return res.send(value);
})


app.listen(port, () => console.log(`SERVER started on localhost:${port}`));
```

id, fieldの二つから登録、取得ができればOK.

そのほかList型などのデータ構造が存在する。

https://redis.io/docs/manual/data-types/

## pub/subを使ってみる

redisにはpubsubによるメッセージングの機能がある。
これを使ってみよう。

先ほどのindex.tsに以下を加える。

```ts:index.ts
redis.on("message", (channel, msg) =>{
    console.log("getMessage");
    console.log({channel});
    console.log({msg});
});
redis.subscribe("pubsubMessage");
```

* redisが```message```イベントを受け取ったとき、その内容を出力する。
* redisが```"pubsubMessage"```チャンネルをsubscribeすることを登録する。

これを動かした状態で、今度はコマンドからこのチャンネルを叩いてみる。redisのコンテナのターミナルにアクセスし、以下を実行する。

```
redis-cli
```
ここから、以下のようにメッセージを送信する。

```
publish pubsubMessage "test"
```
これで先ほど動かしておいたindex.tsの方の出力でこの内容を受け取れていればOK。


## 参考
https://chaika.hatenablog.com/entry/2020/06/29/083000