perf-hot-paths.js 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. /**
  2. * 热路径接口耗时探测:连续两次请求,第二次应为缓存命中。
  3. * 目标:热请求 < 300ms
  4. */
  5. const http = require("http");
  6. function timed(method, path, body) {
  7. const t0 = Date.now();
  8. return new Promise((resolve) => {
  9. const data = body == null ? null : JSON.stringify(body);
  10. const headers = { Accept: "application/json" };
  11. if (data) {
  12. headers["Content-Type"] = "application/json";
  13. headers["Content-Length"] = Buffer.byteLength(data);
  14. }
  15. const r = http.request(
  16. { hostname: "localhost", port: 8088, path: "/sjnmtybt" + path, method, headers },
  17. (res) => {
  18. let raw = "";
  19. res.on("data", (c) => (raw += c));
  20. res.on("end", () => {
  21. const ms = Date.now() - t0;
  22. let code = res.statusCode;
  23. try {
  24. const j = JSON.parse(raw);
  25. code = j.code != null ? j.code : code;
  26. } catch (_) {}
  27. resolve({ ms, code, bytes: raw.length });
  28. });
  29. }
  30. );
  31. r.on("error", (e) => resolve({ ms: Date.now() - t0, code: 0, error: e.message }));
  32. if (data) r.write(data);
  33. r.end();
  34. });
  35. }
  36. const cases = [
  37. ["GET", "/api/regions", null],
  38. ["GET", "/api/amount-standards/current?townId=T_YEXIE", null],
  39. ["GET", "/api/dashboard/region-stats?payMonth=202608&townId=T_YEXIE&months=6", null],
  40. ["GET", "/api/dashboard/summary?payMonth=202608&townId=T_YEXIE", null],
  41. ["GET", "/api/dashboard/workbench?payMonth=202608&townId=T_YEXIE&months=6", null],
  42. [
  43. "POST",
  44. "/api/batches/page",
  45. { pageNum: 1, pageSize: 20, townId: "T_YEXIE", batchLevel: "VILLAGE" },
  46. ],
  47. [
  48. "POST",
  49. "/api/personnel/page",
  50. { pageNum: 1, pageSize: 20, townId: "T_YEXIE", villageId: "V_YX_001" },
  51. ],
  52. [
  53. "POST",
  54. "/api/payments/details/page",
  55. { pageNum: 1, pageSize: 20, townId: "T_YEXIE" },
  56. ],
  57. ["POST", "/api/alerts/page", { pageNum: 1, pageSize: 20, townId: "T_YEXIE" }],
  58. ];
  59. (async () => {
  60. console.log("perf-hot-paths | threshold 300ms (warm)\n");
  61. let fail = 0;
  62. for (const [method, path, body] of cases) {
  63. const cold = await timed(method, path, body);
  64. const warm = await timed(method, path, body);
  65. const ok = warm.ms <= 300;
  66. if (!ok) fail++;
  67. console.log(
  68. (ok ? "OK " : "SLOW") +
  69. " | warm=" +
  70. String(warm.ms).padStart(4) +
  71. "ms cold=" +
  72. String(cold.ms).padStart(4) +
  73. "ms | " +
  74. method +
  75. " " +
  76. path.split("?")[0] +
  77. " | code=" +
  78. warm.code
  79. );
  80. }
  81. console.log("\n" + (fail ? "RESULT: " + fail + " slow endpoints" : "RESULT: all warm < 300ms"));
  82. process.exit(fail ? 1 : 0);
  83. })();