---
title: "解決 Node.js 單執行緒效能瓶頸的幾種實戰解法"
description: "在擴充 Uptime Kuma 負載平衡與 Failover 功能時，踩到 Node.js 單執行緒瓶頸，透過 Worker Threads、Cluster 與 Bun 把多核心效能真正吃滿。"
canonical_url: "https://blog.markkulab.net/post/nodejs-worker-threads-bun-performance"
author: "Mark Ku"
author_url: "https://blog.markkulab.net/author/mark-ku"
site: "Mark Ku's Blog"
date_published: "2025-12-30 01:01:01 +0800"
category: "Backend"
tags: ["nodejs", "bun", "worker threads", "cluster", "performance", "uptime kuma", "monitoring"]
language: "zh-TW"
license: "CC BY 4.0"
license_url: "https://creativecommons.org/licenses/by/4.0/"
attribution: "轉載或引用請註明作者並附上原文連結"
---

# 解決 Node.js 單執行緒效能瓶頸的幾種實戰解法

## 前言

在幫 **Uptime Kuma** 擴充負載平衡（Load Balancing）與節點切換（Failover）功能的過程中，我意外踩到了一個Node.Js的效能瓶頸：

同一套程式，在家裡的機器可以穩穩跑 **1000 個監控**，但是放到一台 **13 年前的測試機** 上時，大概 **單一節點跑到 800 個監控就開始頓、反應變慢**。

這也逼得我回頭重新檢查，底是我寫壞，還是 Node.js 天生的限制？

---


## 為什麼 800 個監控就開始頓？

Uptime Kuma 本身就是一個高頻輪詢（polling）的系統：每個監控 item 都要定期發出請求、判斷成功或失敗、更新狀態，現在又多了一層：

如果運行在新一點的 CPU 上，這些監控還撐得住，但在那台 13 年前的測試機上，**大約 800 個監控** 時，以下現象開始出現：

- 後台 UI 操作有明顯「卡一下」的感覺
- 定時任務排程延遲，比原本設定的時間晚觸發
- 整體 CPU 使用率接近單核心吃滿，但其他核心卻閒著

### 在談關鍵原因前，我們先了解 JS 的Event loop 

JavaScript 本身是單執行緒，所有同步程式碼都必須依序在 call stack 中執行，一旦在主執行緒裡執行了耗時的同步工作（例如大量計算或死迴圈），整個執行緒就會被 block，其他任務完全無法插隊。  

相對地，像 setTimeout、HTTP 請求這類需要等待的操作，並不會直接佔用 call stack。執行環境會先將它們交給底層 API（libuv）處理，等任務完成後，再把對應的 callback 放進佇列中，等待 event loop 在 call stack 空閒時依序取回並執行。  

正因如此，event loop 讓 JavaScript 即使只有一條主執行緒，也能透過非同步機制同時安排大量 I/O 工作，而不會因為等待外部資源就讓整個程式停擺。  

### 關鍵原因
當監控數量很多、輪詢頻率又很高時**，就會出現一堆「需要重複計算」的邏輯一起擠在同一條 event loop 上。這些計算又不是在等 I/O，而是實打實吃 CPU，久了自然就變成效能瓶頸。


因此在不升級單核 CPU 的前提下，我這邊整理出幾種實際可以提昇效能的解法: 

## 解法一：降低輪詢頻率（調整 interval）


在高頻輪詢的架構裡，很多「複雜計劃」其實是被輪詢驅動的。直接降低輪詢頻率（interval）可以讓 CPU bound 的邏輯觸發次數下降，進而讓 event loop 壓力減輕。  

以 Uptime Kuma 為例，每個監控都是一條輪詢任務：例如有 1000 個監控，每 60 秒就要發一次請求、判斷成功／失敗、更新狀態與儀表板。這些動作本身就是吃 CPU 的邏輯，如果 interval 設太短，就會在同一段時間內把一大堆「複雜計劃」一起塞進同一條 event loop。適度拉長輪詢頻率（例如從 15 秒調成 30 秒，或把不那麼關鍵的監控改成更長間隔），可以直接減少這些 CPU-bound 邏輯被觸發的次數，讓 event loop 壓力下降、整體卡頓感也會跟著改善。


---

## 解法二：改成多節點（水平擴充 Uptime Kuma）

當單機資源有限時，將監控分散到多台節點是更穩健的做法。每個節點只負責一部分監控，狀態透過共用儲存（DB/Redis）統一，前面可加反向代理或任務分配層。

參考：我對多節點/Cluster 的思路與做法整理在這篇文章+，可作為延伸閱讀：[Uptime Kuma Cluster 實作筆記](https://blog.markkulab.net/implement-uptime-kuma-cluster-vibe-coding/)

### 實作要點

- 共用儲存：資料庫/Redis 作為單一事實來源，避免節點只放記憶體狀態。
- 任務分片：用標籤、哈希或佇列將監控分配到不同節點；避免重複監控。
- 健康監測與接手：節點故障時，任務能被其他節點自動接手（Failover）。
- 可觀測性：集中化日誌、指標與告警，利於維運與問題定位。

### 效果

- 單機壓力顯著降低，整體吞吐與可用性提升。
- 故障隔離更好，節點出問題不會拖垮整體服務。

---

## 解法三：引入 Bun 作為高效能 Runtime

### Bun 是什麼？
在和同事討論 Uptime Kuma 的時候，他建議我可以試試 Bun，所以我就順勢往下研究了一下。Bun 本質上是一個主打高效能的 JavaScript Runtime，特色大致有：

- JIT / runtime 設計偏向高效能，對啟動與執行效率都有優化
- 內建打包器、測試工具、套件管理（`bun install`）等等
- 對 Node.js API 有一定程度的相容，但不是 100%（這點要特別注意）

---

## 解法四：把複雜計劃丟給 Worker Threads

> 註：此段為我目前的研究紀錄，尚未在專案中完成實作；內容著重思路與示意，非落地方案。

我研究的做法是 **針對「複雜計劃」本身下手**，把最吃 CPU 的那一塊抽出去，丟給 `worker_threads` 處理。

### 適合丟給 Worker Threads 的東西

- 根據監控結果重新計算各節點的 **權重與健康度**
- 執行 **複雜的規則判斷**（例如多條件 failover 策略）
- 對大量監控做 **批次分析 / 排序 / 統計**

概念上就是：主執行緒負責：

- 接收監控結果
- 排隊 / 分派任務給 worker
- 接收 worker 算完的結果，更新狀態

而真正重的邏輯，搬去 worker 檔案裡去跑。

### 程式範例

一個簡化版的程式骨架大概像這樣（示意，不是完整程式）：

```javascript
// main.js
const { Worker } = require("worker_threads");

function recalcLoadBalancing(monitors) {
  return new Promise((resolve, reject) => {
    const worker = new Worker("./recalc-worker.js", {
      workerData: { monitors },
    });

    worker.on("message", (result) => resolve(result));
    worker.on("error", reject);
    worker.on("exit", (code) => {
      if (code !== 0) reject(new Error(`Worker exited: ${code}`));
    });
  });
}
```

```javascript
// recalc-worker.js
const { parentPort, workerData } = require("worker_threads");

function heavyRecalc(monitors) {
  // 在這裡做複雜、吃 CPU 的演算法
  // 回傳新的節點權重 / 排序結果等等
  return { /* ... */ };
}

const result = heavyRecalc(workerData.monitors);
parentPort.postMessage(result);
```

這樣一來：

- 主執行緒不再被「一次算 800 個監控的負載」卡死
- 即使在老舊 CPU 上，UI 頓的感覺會明顯改善
- 要多吃幾顆核心，只要開多幾個 worker 就好（當然要控制數量）

---

## 解法五：Cluster / 多個 Node process 吃滿多核心

> 註：此段為研究方向與可行性分析，尚未實作於現有服務。

如果你的服務本身是 **多使用者、多請求的 Web API**，除了把複雜計劃抽出去，其實也可以再用 **Cluster / 多 process** 來分散負載。

概念類似：

- 用 `cluster` 或 PM2 把同一個 Node.js 應用跑成多個 process
- 例如一台 4 核 CPU，就開 4 個 worker process
- 前面再用一層反向代理（Nginx / HAProxy / Traefik）做負載平衡

### Cluster 範例

```javascript
const cluster = require("cluster");
const os = require("os");

if (cluster.isMaster) {
  const numCPUs = os.cpus().length;
  console.log(`Master process ${process.pid} is running`);
  console.log(`Forking ${numCPUs} workers...`);

  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on("exit", (worker, code, signal) => {
    console.log(`Worker ${worker.process.pid} died, restarting...`);
    cluster.fork();
  });
} else {
  // Worker process - 執行實際的應用邏輯
  require("./app.js");
  console.log(`Worker ${process.pid} started`);
}
```

---

## 總結

| 方案 | 適用場景 | 優點 | 缺點 |
|------|----------|------|------|
| 降低輪詢頻率（interval） | 高頻輪詢導致壓力 | 減少複雜計劃次數、降低主迴圈負載 | 反應時間變慢、可能漏短暫故障 |
| 多節點（水平擴充） | 單機資源有限、需要擴展 | 分散負載、故障隔離、易於橫向擴張 | 跨節點同步與配置複雜度提高 |
| Bun | 新腳本、獨立服務 | 執行速度快、啟動快 | 相容性還不是 100% |
| Worker Threads | CPU bound 複雜計劃 | 不阻塞主執行緒、可利用多核心 | 需要處理資料序列化 |
| Cluster | 多請求 Web 服務 | 多 process 分散負載、容錯性高 | 狀態需共用儲存 |

如果你現在也在幫其他 Node.js 處理效能問題，又剛好卡在老機器跑不動，可以試試以上方法。

---

## 關於本文與作者

本文出自 [Mark Ku's Blog](https://blog.markkulab.net/post/nodejs-worker-threads-bun-performance)

授權條款： [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) — 轉載或引用請註明作者並附上原文連結

### 關於作者

**[Mark Ku](https://blog.markkulab.net/author/mark-ku)** — Software Solution Provider

- 10+ 年資深軟體工程師，現為 AI 應用 Builder
- 專注大型平台架構設計，從北美電商到AI SaaS訂閱收費系統
- 結合 AI Agent 與自動化，打造高效可演進的產品技術基礎

### 作者開發的免費工具

以下工具皆可免費使用：

- [免費 PDF 簽名工具](https://blog.markkulab.net/tools/pdf-sign): 線上 PDF 簽名工具，瀏覽器內完成手繪、打字、上傳簽名，可拖曳放置、縮放、下載。所有處理都在你的裝置完成，檔案不會上傳。
- [VS Code Refactory](https://blog.markkulab.net/tools/refactory): Refactory 是一款 VS Code 重構擴充套件：34 個重構動作、37 條 code smell 檢查、Code Health 儀表板、18 種語言、534 支測試。懂你的專案慣例：介面放哪、DI 註冊寫在哪、'use client' 該不該加；還會用 git 修改頻率 × 複雜度排出「該先修哪個檔案」，並一鍵把壞味道交給你自己電腦上的 Claude Code 修。免費使用，原始碼不離開你的機器。
- [DB-Kit 資料庫管理工具](https://blog.markkulab.net/tools/db-kit): DB-Kit 是一個用 Tauri + Rust + React 打造的輕量跨平台資料庫管理工具，用單一一致的介面同時管理 MySQL、MariaDB、PostgreSQL、SQL Server、Oracle、SQLite、MongoDB、Redis、Kafka、Elasticsearch 與 RabbitMQ 十一種資料來源：連線密碼以 OS keychain 加密、SSH Tunnel、完整 CRUD、視覺化查詢建構器、多結果集同時顯示、跨連線資料傳輸與比對同步、Excel / CSV 匯入匯出、執行計畫視覺化、ER 圖、排程備份、SQL 壓力測試（p50～p99 延遲百分位）、15 條規則的 SQL 審查、Kafka 訊息瀏覽與監控告警；繁中 / 英文雙語介面，內建 AI 助手（自然語言生成 SQL、AI 審查與調校建議）與命令列工具 dbk。免費開源（MIT），提供 Windows / macOS / Linux 安裝檔。
- [VS Code Super Mermaid](https://blog.markkulab.net/tools/super-mermaid): Super Mermaid 是一款 VS Code 擴充套件：開箱即用的漂亮 Mermaid 圖表，自動上色、即時預覽、滑鼠平移縮放、PNG / SVG 高解析匯出，內建 21 種範本與多種主題。免費開源（MIT）。
- [React Super Mermaid](https://blog.markkulab.net/tools/react-super-mermaid): react-super-mermaid 是一個開源 React 元件庫：一行 <MermaidViewer> 即可渲染漂亮的 Mermaid 圖表，內建 colorful / sketch 主題、平移縮放、圖內搜尋、SVG / PNG 高解析匯出。輕量、SSR 安全、完整 TypeScript 型別。免費開源（MIT）。
- [Jira / Confluence Super Mermaid](https://blog.markkulab.net/tools/jira-super-mermaid): Atlassian Forge app：在 Jira issue 與 Confluence 內文直接寫 Mermaid 語法，畫流程圖、時序圖、狀態機與甘特圖。11 種圖表、SVG / PNG 匯出、明暗主題、完整中日韓文字支援。取得 Runs on Atlassian 資格：圖表存在你自己的站台，app 不呼叫任何第三方服務。免費，即將上架 Atlassian Marketplace。
- [Mermaid 線上預覽](https://blog.markkulab.net/tools/mermaid-preview): 在瀏覽器裡寫 Mermaid、即時看圖，整張圖表壓進網址就能分享。免註冊、不上傳伺服器，相容 mermaid.live 的分享連結。
- [React Intl Phone Number](https://blog.markkulab.net/tools/react-intl-phone-number): react-intl-phone-number 是一個開源 React 元件：framework-agnostic、不依賴 antd，提供 E.164 進出、可搜尋國旗 / 國碼下拉、可配置驗證等級（strict / mobile-strict / loose）、可主題化 CSS 與 i18n，電話邏輯由 google-libphonenumber 驅動。輕量、完整 TypeScript 型別。免費開源（MIT）。
- [Uptime Kuma Cluster](https://blog.markkulab.net/tools/uptime-kuma-cluster): 把單機版 Uptime Kuma 改造成高可用叢集：OpenResty + Lua 智慧負載平衡、MariaDB 共享狀態、健康檢查與自動 Failover，附叢集管理 REST API，一行 Docker Compose 啟動。免費開源（MIT）。
- [特教專案](https://blog.markkulab.net/education): 為特殊教育學生製作的學習教材

### 每日 Podcast

- [科技新鮮事](https://blog.markkulab.net/category/tech-news): 每日精選 AI 與科技趨勢，透過語音摘要快速掌握最新技術動態，涵蓋 AI 應用、軟體架構、DevOps 與工程實戰。 — RSS: https://blog.markkulab.net/feed.xml
- [AI股市蝦聊](https://blog.markkulab.net/category/ai-stock-chat): 每個交易日用 AI 分析台股盤勢，以雙人對話聊當天的盤中觀察與隔日預測。 — RSS: https://blog.markkulab.net/ai-stock-chat/feed.xml

### 電子報

[訂閱電子報](https://blog.markkulab.net/subscribe) — 第一時間收到新文章通知，無垃圾信、隨時可取消訂閱。
