未経験からエンジニア 奮闘記

未経験からエンジニアに自由に生きる途中

MENU

Redis バックグラウンド処理をする仕組みを理解する

知りたかったこと

  • Redisを活用したバックグラウンド処理の管理

結論

下記のフローを理解してると良さそう

  • キューにタスクを追加 (lpush):
  • バックグラウンドプロセスでタスクをポーリング:
  • タスクを処理する関数を呼び出す

また、Redisの基本的な特徴も把握しておくと良い

  • RedisはNoSQLデータベース
  • インメモリ型
  • シングルスレッドで、並列処理をする場合はシャーディング(複数Redis立ち上げる)

背景

  • 今仕事で開発中のタスクで処理が重たいので、バックグラウンド処理を行うようにしている。ただ、ローカル環境でのデバックが難しかったので、しっかり理解しようと思った

サンプルコード

ChatGPTが教えてくれたコード

project-folder/
├── docker-compose.yml
└── app/
    ├── package.json
    ├── app.js
  • docker-compose
version: '3'
services:
  redis:
    image: "redis:latest"
    ports:
      - "6379:6379"

バージョンが4系だと以降に記載するコードは動かない

{
  "dependencies": {
    "redis": "^3.1.2"
  }
}
  • app.js
const redis = require('redis');

// Redisクライアントの作成
const client = redis.createClient({ host: 'localhost' }); // Docker Composeのサービス名


const someProcess = () => {
  for (  var i = 0;  i < 10;  i++  ) {
    console.log(i)
  }
}

// タスクのスケジューリング
const scheduleTask = (taskData) => {
  const taskID = `task:${taskData.id}`;
  client.hmset(taskID, taskData);
  client.lpush('task_queue', taskID);
};

// タスクの処理
const processTask = (taskID) => {
  client.hgetall(taskID, (err, taskData) => {
    if (err) throw err;
    console.log('Processing task:', taskData);
    someProcess()
    setTimeout(() => {
      const result = 'Task completed successfully';
      client.set(`task_result:${taskID}`, result);
      console.log('Task completed:', result);
    }, 3000); // 3秒間のタスクをシミュレート
  });
};

// タスクを処理するループ
const processTasksLoop = () => {
  client.rpop('task_queue', (err, taskID) => {
    if (err) throw err;
    if (taskID) {
      console.log(`taskID ${taskID}`)
      processTask(taskID);
    }
    setTimeout(processTasksLoop, 1000); // 新しいタスクを待つ
  });
};

// テスト用タスクのスケジューリング
scheduleTask({ id: 'task123', param1: 'value1', param2: 'value2' });
scheduleTask({ id: 'task234', param1: 'value2', param2: 'value3' });
scheduleTask({ id: 'task567', param1: 'value3', param2: 'value4' });

// タスクの処理を開始
processTasksLoop();
  • 実行結果

ポーリングされる時に関数を挟むと実行できるだな

taskID task:task123
Processing task: { id: 'task123', param1: 'value1', param2: 'value2' }
0
1
2
3
4
5
6
7
8
9
taskID task:task234
Processing task: { id: 'task234', param1: 'value2', param2: 'value3' }
0
1
2
3
4
5
6
7
8
9
taskID task:task567
Processing task: { id: 'task567', param1: 'value3', param2: 'value4' }
0
1
2
3
4
5
6
7
8
9
Task completed: Task completed successfully
Task completed: Task completed successfully
Task completed: Task completed successfully

Redisのメソッド

  • client.set

キューに対して、データをセットするメソッド

  • client.hmset(今は非推奨)

ハッシュ(Hash)としてデータを設定するメソッド

  • client.lpush

リスト(List)に値を追加z

調べた内容

Redisとは

qiita.com

この記事によると

  • データベース
  • インメモリキャッシュなのが味噌っぽい
  • NoSQL
  • NoSQLだが扱える型は決まっているっぽい
  • シングルスレッド: 複数のリクエストが来ても、順番に処理される
  • シャーディング可能

気になった点

  • データベースなのになんで Mysqlとかと同じ感じで出てこないのか?

自分で結論づけたのは、インメモリキャッシュだから、サーバーが落ちると消えるのでMysqlなどとは同じ扱いにならないのではないか?と思った

  • シングルスレッドについて

Redisって重い処理をバックグランド処理に使われているイメージなんだけど、 シングルスレッドで実行されているので、都合悪いケースが多いのではないか? と思った。

そこは、マルチスレッドではなく、シャーディングという方法があるらしく、 複数台のredisサーバーを立てて、分散させるっぽい

複数台のサーバーの管理で肝になるのが 「slot」で、idnのようなもので管理している