負荷テスト
Socket.IO は、ハンドシェイク、ハートビート、カスタムパケットエンコーディングを含む、独自のプロトコルを持っているため、Socket.IO サーバーの負荷テストを行う最も簡単な方法は、Socket.IO クライアントライブラリを使用して、多数のクライアントを作成することです。
これを行うには、2つの古典的なソリューションがあります。
- Artilleryを使用する
- またはクライアントを手動で管理する
Artillery
Artilleryは、アプリケーションの負荷テストを行うための優れたツールです。接続の作成、イベントの送信、確認応答の確認が可能です。
ドキュメントはこちらにあります。
重要な注意: デフォルトのインストールには、v3/v4 サーバーと互換性のない v2 クライアントが付属しています。このためには、カスタムエンジンをインストールする必要があります: https://github.com/ptejada/artillery-engine-socketio-v3
インストール
$ npm install artillery artillery-engine-socketio-v3
サンプルシナリオ
# my-scenario.yml
config:
  target: "https://:3000"
  phases:
    - duration: 60
      arrivalRate: 10
  engines:
   socketio-v3: {}
scenarios:
  - name: My sample scenario
    engine: socketio-v3
    flow:
      # wait for the WebSocket upgrade (optional)
      - think: 1
      # basic emit
      - emit:
          channel: "hello"
          data: "world"
      # emit an object
      - emit:
          channel: "hello"
          data:
            id: 42
            status: "in progress"
            tags:
              - "tag1"
              - "tag2"
      # emit in a custom namespace
      - namespace: "/my-namespace"
        emit:
          channel: "hello"
          data: "world"
      # emit with acknowledgement
      - emit:
          channel: "ping"
        acknowledge:
          match:
            value: "pong"
      # do nothing for 30 seconds then disconnect
      - think: 30
このシナリオを実行するには
$ npx artillery run my-scenario.yml
Artilleryには、メトリクスをさまざまなエンドポイントに公開する機能や、AWSからテストを実行する機能など、多くの優れた機能も付属しています。
その唯一の制限は、Artillery DSLが従来のクライアントからサーバーへの通信に適しているため、サーバーからクライアントへのイベントを簡単にテストできないことです。そのため、次のセクションに進みます。
クライアントの手動作成
以下は、1000個のSocket.IOクライアントを作成し、1秒あたりに受信したパケット数を監視する基本的なスクリプトです。
const { io } = require("socket.io-client");
const URL = process.env.URL || "https://:3000";
const MAX_CLIENTS = 1000;
const POLLING_PERCENTAGE = 0.05;
const CLIENT_CREATION_INTERVAL_IN_MS = 10;
const EMIT_INTERVAL_IN_MS = 1000;
let clientCount = 0;
let lastReport = new Date().getTime();
let packetsSinceLastReport = 0;
const createClient = () => {
  // for demonstration purposes, some clients stay stuck in HTTP long-polling
  const transports =
    Math.random() < POLLING_PERCENTAGE ? ["polling"] : ["polling", "websocket"];
  const socket = io(URL, {
    transports,
  });
  setInterval(() => {
    socket.emit("client to server event");
  }, EMIT_INTERVAL_IN_MS);
  socket.on("server to client event", () => {
    packetsSinceLastReport++;
  });
  socket.on("disconnect", (reason) => {
    console.log(`disconnect due to ${reason}`);
  });
  if (++clientCount < MAX_CLIENTS) {
    setTimeout(createClient, CLIENT_CREATION_INTERVAL_IN_MS);
  }
};
createClient();
const printReport = () => {
  const now = new Date().getTime();
  const durationSinceLastReport = (now - lastReport) / 1000;
  const packetsPerSeconds = (
    packetsSinceLastReport / durationSinceLastReport
  ).toFixed(2);
  console.log(
    `client count: ${clientCount} ; average packets received per second: ${packetsPerSeconds}`
  );
  packetsSinceLastReport = 0;
  lastReport = now;
};
setInterval(printReport, 5000);
これを、独自のアプリケーションの負荷テストの出発点として使用できます。