چگونه در node و nginx در حالت توزیع شده از socket-io استفاده کنیم
فرض میکنم که شما هم دارید از pm2 برای مدیریت پروسس های node اتون استفاده میکنید
در این صورت با یه همچین دستوری برنامه اتون رو روی یه سرور scale میکنید
pm2 start app.js -i 4 --name myapp
مشکلی که در این حالت برای استفاده از socket وجود داره اینه که ما الان 4 تا پروسس داریم که درخواست های ورودی رو پردازش میکنند. این پروسس ها کلا با هم متفاوت هستند و گویی انگار رو یک سرور جدا هستند. وقتی کلاینت یه درخواستی رو در یه کانال socket.io ارسال میکنه، ممکنه که درخواست به یکی از پروسس هایی که اصلا توش اون کانال وجود نداره ارسال بشه.
پس باید یه جوری همیشه هر کلاینت رو به اون پروسسی وصل کنیم که توش اون socket ایجاد شده و اون کانال تو اون پروسس وجود داره.
وقتی pm2 پروسس ها رو ایجاد میکنه نمیشه بینشون تمیز قایل شد؛ و مثلا یک درخواست رو به یک پروسس خاص وصل کرد. برای همین هر پروسس رو روی یه پورت bind میکنیم و اونوقت میتونیم از یه load balancer مثل nginx استفاده کنیم و اون رو طوری کانفیگ کنیم که هر کلاینت رو بر اساس ip اش همیشه به اون پروسسی وصل کنه که اولین بار به اون هدایت شده. یه خورده گیج کننده بود نه؟ بزارید با مثال جلو بریم.
اول ببینیم چطور میشه چندتا پروسس رو روی پورت های مختلف بالا آورد؟ اونم با pm2. اونم به شکلی که همه پورت ها رو هارد کد نکنیم. وقتی از دستور scale در pm2 استفاده میکنیم، یه مقداری رو در env ست میکنه به نامNODE_APP_INSTANCE
که ایندکس اون پروسس هست. مثلا تو تکه کد اول که 4 تا پروسس داریم، تو اولین پروسس مقدار NODE_APP_INSTANCE
صفره و آخری مقدارش 3 خواهد بود. پس میتونیم کد زیر رو داشته باشیم
var express = require('express');
var app = express();
var http = require('http').Server(app)
const io = require('socket.io')(http)
http.listen(
8000 + parseInt(process.env.NODE_APP_INSTANCE), // <-- specify port
"0.0.0.0",
function () {
console.log('nodejs server started', server.address().port);
}
);
اینجوری وقتی از دستور scale در pm2 استفاده میکنیم، هر پروسس روی یک پورت اجرا میشه. حالا وقت اونه که nginx رو کانفیگ کنیم. از اون جایی که چندتا پروسس داریم، باید از ویژگی load balancer در nginx استفاده کنیم. برای اینکار اول یه upstream در بخش http تعریف میکنیم
#in http{} section
upstream myapp {
ip_hash; # enable sticky session
server 127.0.0.1:8000;
server 127.0.0.1:8001;
}
به اون ip_hash دقت کنید. این خط سیاست load balancer رو مشخص میکنه و نشون میده که هر درخواست براساس ip اش همیشه به یکی از پروسس ها route بشه. مقادیر دیگه ای هم میتونه داشته باشه مثل least_conn که ریکوئست جدید رو میفرسته اونجا که کانکشن کمتری بهش هست.
در آخر باید یه reverse proxy برای Nginx کانفیگ کنیم که درخواست ها به سمت این پروسس ها بیان که اونم اینجوریه
server {
listen 80;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://nodes;
# enable WebSockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
به اون سه خط آخر دقت کنید که خیلی مهمه. به خصوص آخری
فقط کافیه که برنامه اتون رو اجرا کنید و nginx رو ریستارت کنید. حالا شما یه سیستم توزیع شده در سطح کورهای cpu دارید. بهتره که تعداد پروسس هایی که برای scale استفاده میکنید برابر با core های cpu اتون باشه
والسلام
منابع
https://github.com/Unitech/PM2/issues/1510
https://socket.io/docs/using-multiple-nodes/
https://github.com/socketio/socket.io/tree/master/examples/cluster-nginx
مطلبی دیگر از این انتشارات
جوری کد بنویس که بشه خوند...!
مطلبی دیگر از این انتشارات
مقایسه سطحی و عمیق در جاواسکریپت
مطلبی دیگر از این انتشارات
هر آنچه در سال 2018 برای توسعه Front-End اتفاق افتاد