负载测试

由于 Socket.IO 有自己的协议,包括握手、心跳和自定义数据包编码,负载测试 Socket.IO 服务器最简单的方法是使用 Socket.IO 客户端库并创建大量客户端。

有两种经典的解决方案可以做到这一点:

Artillery

Artillery 是负载测试应用程序的绝佳工具。它允许创建连接、发送事件和检查确认。

文档可以在这里找到。

重要提示:默认安装带有 v2 客户端,与 v3/v4 服务器不兼容。您需要为此安装自定义引擎:https://github.com/ptejada/artillery-engine-socketio-v3

安装:

  1. $ npm install artillery artillery-engine-socketio-v3

示例场景:

  1. # my-scenario.yml
  2. config:
  3. target: "http://localhost:3000"
  4. phases:
  5. - duration: 60
  6. arrivalRate: 10
  7. engines:
  8. socketio-v3: {}
  9. scenarios:
  10. - name: My sample scenario
  11. engine: socketio-v3
  12. flow:
  13. # wait for the WebSocket upgrade (optional)
  14. - think: 1
  15. # basic emit
  16. - emit:
  17. channel: "hello"
  18. data: "world"
  19. # emit an object
  20. - emit:
  21. channel: "hello"
  22. data:
  23. id: 42
  24. status: "in progress"
  25. tags:
  26. - "tag1"
  27. - "tag2"
  28. # emit in a custom namespace
  29. - namespace: "/my-namespace"
  30. emit:
  31. channel: "hello"
  32. data: "world"
  33. # emit with acknowledgement
  34. - emit:
  35. channel: "ping"
  36. acknowledge:
  37. match:
  38. value: "pong"
  39. # do nothing for 30 seconds then disconnect
  40. - think: 30

要运行此方案:

  1. $ npx artillery run my-scenario.yml

Artillery 还附带了许多很棒的功能,例如能够将指标发布到各种端点从 AWS 运行测试

它唯一的限制是您无法轻松测试服务器到客户端的事件,因为 Artillery DSL 更适合经典的客户端到服务器通信。这将我们带到下一节.

手动创建客户端

这是创建一千个 Socket.IO 客户端并监控每秒接收的数据包数量的基本脚本:

  1. const { io } = require("socket.io-client");
  2. const URL = process.env.URL || "http://localhost:3000";
  3. const MAX_CLIENTS = 1000;
  4. const POLLING_PERCENTAGE = 0.05;
  5. const CLIENT_CREATION_INTERVAL_IN_MS = 10;
  6. const EMIT_INTERVAL_IN_MS = 1000;
  7. let clientCount = 0;
  8. let lastReport = new Date().getTime();
  9. let packetsSinceLastReport = 0;
  10. const createClient = () => {
  11. // for demonstration purposes, some clients stay stuck in HTTP long-polling
  12. const transports =
  13. Math.random() < POLLING_PERCENTAGE ? ["polling"] : ["polling", "websocket"];
  14. const socket = io(URL, {
  15. transports,
  16. });
  17. setInterval(() => {
  18. socket.emit("client to server event");
  19. }, EMIT_INTERVAL_IN_MS);
  20. socket.on("server to client event", () => {
  21. packetsSinceLastReport++;
  22. });
  23. socket.on("disconnect", (reason) => {
  24. console.log(`disconnect due to ${reason}`);
  25. });
  26. if (++clientCount < MAX_CLIENTS) {
  27. setTimeout(createClient, CLIENT_CREATION_INTERVAL_IN_MS);
  28. }
  29. };
  30. createClient();
  31. const printReport = () => {
  32. const now = new Date().getTime();
  33. const durationSinceLastReport = (now - lastReport) / 1000;
  34. const packetsPerSeconds = (
  35. packetsSinceLastReport / durationSinceLastReport
  36. ).toFixed(2);
  37. console.log(
  38. `client count: ${clientCount} ; average packets received per second: ${packetsPerSeconds}`
  39. );
  40. packetsSinceLastReport = 0;
  41. lastReport = now;
  42. };
  43. setInterval(printReport, 5000);

您可以将其用作负载测试您自己的应用程序的起点。