ایجاد Load Balancing در Node.js
با رشد ترافیک و نیاز به پاسخگویی سریع، توزیع بار یا Load Balancing به یکی از اجزای کلیدی معماریهای مقیاسپذیر تبدیل شده است. در اکوسیستم Node.js که پردازش رویدادمحور و تکنخی (single-threaded) دارد، استفاده از راهکارهای متنوع برای توزیع درخواستها بین چند نمونه (process/instance) ضروری است. در این مقاله به طور جامع به راهکارهای متداول، مثالهای عملی، مزایا و معایب، و نکات بهینهسازی میپردازیم.
مفاهیم پایه
- Load Balancer: میانافزاری که ترافیک ورودی را بین چند سرور/فرآیند تقسیم میکند.
- Round-robin, Least connections, IP hash: الگوریتمهای رایج توزیع بار.
- Sticky Session: نگهداری Session کاربر روی یک نمونه برای حفظ ارتباطات stateful مثل WebSocket.
چرا در Node.js نیاز به Load Balancing داریم؟
Node.js خود بهصورت تکنخی از یک هستهٔ CPU استفاده میکند. روی سرورهای چند هستهای، بهترین عملکرد زمانی حاصل میشود که چند فرآیند Node روی هستههای مختلف اجرا شوند. Load Balancer درخواستها را بین این فرآیندها توزیع میکند، بهبود کارایی، افزونگی و تحمل خطا فراهم میآورد.
روشهای معمول پیادهسازی
- استفاده از ماژول داخلی cluster در Node.js
- ابزارهای process manager مانند PM2 (mode cluster)
- Reverse proxy مثل Nginx یا HAProxy
- Load Balancerهای لایهٔ 4 شبکه (مثل AWS ELB) یا لایهٔ 7 (Application Load Balancer)
1. مثال ساده با Node.js cluster
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
for (let i = 0; i {
console.log(`worker ${worker.process.pid} died, forking a new one`);
cluster.fork();
});
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end(`Handled by worker ${process.pid}`);
}).listen(3000);
}این کد از ماژول cluster استفاده میکند تا به تعداد هستههای CPU، فرزند (worker) بسازد. هر worker یک سرور HTTP جداگانه روی پورت 3000 اجرا میکند؛ اما master ترافیک را بین آنها توزیع میکند و در صورت مرگ هر worker، آن را دوباره میسازد.
نکات تکمیلی در استفاده از cluster
- cluster مناسب برنامههای stateless است؛ در صورت نیاز به نگهداری session از مکانیزم ذخیرهسازی مرکزی (Redis، DB) استفاده کنید.
- برای graceful shutdown، قبل از خروج worker جدیدی بسازید یا اتصالات را بهصورت ترتیبی ببندید.
2. استفاده از Nginx بهعنوان Reverse Proxy / Load Balancer
upstream node_app {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://node_app;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}در این تنظیم Nginx درخواستهای ورودی را به چند نمونه Node هدایت میکند. برای WebSocketها باید proxy_set_headerها و upgrade را تنظیم کنید. Nginx امکان انتخاب الگوریتمهای مختلف (مثل least_conn) و health checkهای ساده را فراهم میکند.
Sticky Session و Socket.io
# nginx sticky by ip
upstream node_app {
ip_hash;
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}استفاده از ip_hash در Nginx یک روش ساده برای sticky session است؛ اما دقیق نیست و در صورت تغییر IP کاربر نمیتواند برقراری پیوستگی را تضمین کند. برای WebSocket و socket.io معمولاً راهکارهای حرفهایتر مانند sticky-session یا پروکسی TCP در لایهٔ 4 پیشنهاد میشود.
مقایسه روشها
| روش | مزایا | معایب |
|---|---|---|
| Node cluster | ساده، بدون نیاز به ابزار خارجی | محدود به یک ماشین، مدیریت کمتر پیشرفته |
| Nginx/HAProxy | پایدار، کنترل تراکم، الگوریتمهای متعدد | نیاز به پیکربندی؛ برای sticky و websocket باید دقت کرد |
| Cloud LB (ELB/ALB) | مقیاس خودکار، مانیتورینگ، قابلیتهای امنیتی | هزینه، وابستگی به ابر |
بهینهسازی و نکات عملیاتی
- از یک session store مرکزی (Redis یا DB) برای اطلاعات stateful استفاده کنید.
- فعالسازی keep-alive بین Load Balancer و backend برای کاهش هزینهٔ اتصال.
- پیادهسازی health checks و readiness probes (در Kubernetes از Liveness/Readiness استفاده کنید).
- برای WebSocket از load balancer لایه 4 یا sticky sessions امن استفاده کنید.
- قابلیت graceful restart: اجازه دهید workerهای قدیمی اتصالات جاری را کامل کنند قبل از خاتمه.
نمونه استفاده از PM2 برای راهاندازی کلستر
# نصب و اجرای برنامه در حالت cluster با چند فرزند
pm2 start app.js -i max
# نمایش وضعیت
pm2 list
# تنظیمات ذخیرهسازی و reload بیوقفه
pm2 reload all --update-envPM2 یک process manager قدرتمند برای Node است که با دستور بالا بهصورت خودکار تعداد فرزند را متناسب با هستههای CPU ایجاد میکند. قابلیتهایی مانند مانیتورینگ، لاگ مرکزی و reload بدون قطعی را فراهم میکند.
موارد واقعی و معماری پیشنهادی
برای یک اپلیکیشن واقعی توصیه میشود:
- در سطح سرور: چندین نمونه Node (با PM2 یا Docker) در هر ماشین اجرا کنید.
- در سطح ماشین: Nginx/HAProxy به عنوان reverse proxy و load balancer لایهٔ 7 قرار دهید.
- برای مقیاس افقی: از یک Load Balancer cloud یا Kubernetes Ingress استفاده کنید.
- Sessionها، cache و pub/sub را به Redis منتقل کنید تا state بین نمونهها مشترک شود.
جمعبندی و توصیههای تخصصی
Load Balancing در Node.js یک مسئلهٔ چندوجهی است که بسته به نیازهای شما (stateless vs stateful، WebSocket، محیط ابری یا on-premise) راهکارهای متفاوتی میطلبد. ترکیب استفاده از cluster یا PM2 برای استفاده بهینه از CPU، بهعلاوهٔ Nginx/HAProxy برای مدیریت ترافیک و استفاده از Redis برای ذخیرهٔ state، یک الگوی عملی و قابل اعتماد است. پیادهسازی تست بار (load testing) و مانیتورینگ پیوسته را فراموش نکنید تا نقطهٔ گلوگاه و رفتار سیستم تحت بار واقعی مشخص شود.
در صورت نیاز میتوان نمونههای پیشرفتهتر شامل Kubernetes Ingress، سرویس Mesh، یا پیکربندیهای Health Check/Autoscaling را نیز بررسی و پیادهسازی کرد.
آیا این مطلب برای شما مفید بود ؟




