0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NodIO実装ハンズオン:サーバ&クライアント構築

Last updated at Posted at 2025-08-16

はじめに

本記事では、Node.js+Redisを用いたオーケストレーションサーバと、ブラウザクライアント(Web Worker含む)を実際に構築する手順を解説します。ソースコードを動かしながら、NodIOのコア実装を体験しましょう。

目次

  1. 環境準備
  2. オーケストレーションサーバ実装
    1. プロジェクト初期化
    2. WebSocketサーバ構築
    3. Redisタスクキュー連携
    4. タスク配信・結果受信ロジック
  3. ブラウザクライアント実装
    1. HTML & メインスクリプト
    2. Web Worker スクリプト
  4. 動作確認
  5. 次のステップ

1. 環境準備

  • Node.js (v16+)
  • Redis サーバ
  • ブラウザ(最新Chrome推奨)
# プロジェクト用フォルダ作成
mkdir nodio-hands-on && cd nodio-hands-on

# Node.js プロジェクト初期化
npm init -y

# 必要パッケージのインストール
npm install express ws ioredis

2. オーケストレーションサーバ実装

2.1 プロジェクト構成

nodio-hands-on/
├─ server/
│  ├─ index.js
│  └─ tasks.js
└─ client/
   ├─ index.html
   ├─ main.js
   └─ worker.js

2.2 WebSocketサーバ構築 (server/index.js)

const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const { enqueueTask, dequeueTask } = require('./tasks');

const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });
const clients = new Map(); // nodeId → ws

let nextNodeId = 1;

wss.on('connection', socket => {
  const nodeId = nextNodeId++;
  clients.set(nodeId, socket);
  console.log(`Node ${nodeId} connected`);

  socket.on('message', async message => {
    const msg = JSON.parse(message);
    if (msg.type === 'RESULT') {
      console.log(`Result from ${nodeId}:`, msg.payload);
      // TODO: 検証・集約ロジック
    }
  });

  socket.on('close', () => {
    clients.delete(nodeId);
    console.log(`Node ${nodeId} disconnected`);
  });
});

// 定期タスク配信ループ
setInterval(async () => {
  const task = await dequeueTask();
  if (!task) return;
  for (const [nodeId, ws] of clients) {
    ws.send(JSON.stringify({ type: 'TASK', payload: task }));
    break; // 単一ノードへ配信
  }
}, 100);

server.listen(8080, () => console.log('Server listening on 8080'));

2.3 Redisタスクキュー連携 (server/tasks.js)

const Redis = require('ioredis');
const redis = new Redis();

// サンプルタスク投入(起動時一度だけ)
(async () => {
  await redis.lpush('tasks', JSON.stringify({ taskId: 't1', data: 42 }));
  await redis.lpush('tasks', JSON.stringify({ taskId: 't2', data: 84 }));
})();

async function dequeueTask() {
  const raw = await redis.rpop('tasks');
  return raw ? JSON.parse(raw) : null;
}

async function enqueueTask(task) {
  await redis.lpush('tasks', JSON.stringify(task));
}

module.exports = { dequeueTask, enqueueTask };

3. ブラウザクライアント実装

3.1 HTML & メインスクリプト (client/index.html + client/main.js)




  
  NodIO Client


  NodIO クライアント
  


// client/main.js
const socket = new WebSocket('ws://localhost:8080');

socket.addEventListener('open', () => console.log('WebSocket connected'));
socket.addEventListener('message', e => {
  const msg = JSON.parse(e.data);
  if (msg.type === 'TASK') {
    const worker = new Worker('worker.js');
    worker.postMessage(msg.payload);
    worker.onmessage = ev => {
      socket.send(JSON.stringify({ type: 'RESULT', payload: ev.data }));
    };
  }
});
window.addEventListener('beforeunload', () => {
  socket.close();
});

3.2 Web Worker スクリプト (client/worker.js)

// client/worker.js
self.onmessage = e => {
  const { taskId, data } = e.data;
  // 例:データを二乗して返す簡易計算
  const result = data * data;
  postMessage({ taskId, result });
};

4. 動作確認

  1. Redisサーバ起動:redis-server
  2. サーバ起動:node server/index.js
  3. ブラウザで client/index.html を開く
  4. サーバコンソールにタスク配信・結果受信ログを確認

5. 次のステップ

  • フェイルオーバー:複数ノードへの同時配信とクロスチェック
  • 検証ロジック:結果ハッシュ比較や閾値判定
  • ダッシュボード:Prometheus/Grafana連携によるノード・タスク状況可視化
  • WebAssembly対応:worker内をWASM化して高速化

シリーズ3では、こうしたパフォーマンス計測と最適化手法を詳述します。お楽しみに!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?