ساخت سرور چت گروهی در Node.js
چت گروهی (group chat) یکی از رایجترین قابلیتها در اپلیکیشنهای مدرن است. در این مقاله به صورت گامبهگام معماری، کد نمونه، نکات امنیتی و روشهای مقیاسپذیری برای ساخت یک سرور چت گروهی در Node.js را بررسی میکنیم. هدف، ارائه راهکار عملی، قابل توسعه و تولیدی است.
نیازمندیهای پایه
- ارتباط بلادرنگ: WebSocket یا Socket.IO
- احراز هویت: JWT یا session
- ذخیره پیامها: MongoDB یا هر دیتابیس NoSQL
- مقیاسپذیری: Redis برای انتشار/اشتراک در چند سرور
- امنیت و اعتبارسنجی ورودیها
نمونه ساده با Socket.IO و Express
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const jwt = require('jsonwebtoken');
const app = express();
const server = http.createServer(app);
const io = new Server(server, {
cors: { origin: "https://your-client.com", methods: ["GET", "POST"] }
});
// middleware برای احراز هویت با JWT
io.use((socket, next) => {
const token = socket.handshake.auth?.token;
if (!token) return next(new Error("Authentication error"));
try {
const user = jwt.verify(token, process.env.JWT_SECRET);
socket.user = user;
next();
} catch (err) {
next(new Error("Authentication error"));
}
});
io.on('connection', (socket) => {
socket.on('joinRoom', (roomId) => {
socket.join(roomId);
socket.to(roomId).emit('userJoined', { userId: socket.user.id });
});
socket.on('message', ({ roomId, text }) => {
const message = {
roomId,
sender: socket.user.id,
text,
createdAt: new Date()
};
// اینجا باید پیام در دیتابیس ذخیره شود
io.to(roomId).emit('message', message);
});
socket.on('disconnect', () => {
// مدیریت خروج کاربر
});
});
server.listen(3000);در این نمونه، از Express برای راهاندازی HTTP و از Socket.IO برای ارتباط بلادرنگ استفاده شده است. لایهٔ میانی (middleware) برای اعتبارسنجی JWT به کار رفته تا فقط کاربران معتبر بتوانند وصل شوند. هنگام ارسال پیام، پیام به اعضای اتاق مربوطه پخش میشود.
ذخیره پیام در MongoDB — مدل ساده
const mongoose = require('mongoose');
const MessageSchema = new mongoose.Schema({
roomId: { type: String, required: true, index: true },
sender: { type: String, required: true },
text: { type: String, required: true },
createdAt: { type: Date, default: Date.now }
});
module.exports = mongoose.model('Message', MessageSchema);این مدل ساده پیامها را بر اساس roomId و sender ذخیره میکند. در سیستم واقعی ممکن است فیلدهایی مانند attachments، status (delivered/read)، و reactions اضافه شوند.
مقیاسپذیری افقی با Redis Adapter
در محیط تولیدی، معمولاً چندین instance از سرور Node.js اجرا میشود. برای هماهنگسازی رویدادهای Socket.IO بین این instanceها از Redis Pub/Sub و adapter مخصوص استفاده میکنیم.
const { createAdapter } = require('@socket.io/redis-adapter');
const { createClient } = require('redis');
(async () => {
const pubClient = createClient({ url: 'redis://localhost:6379' });
const subClient = pubClient.duplicate();
await pubClient.connect();
await subClient.connect();
io.adapter(createAdapter(pubClient, subClient));
})();کد بالا Redis را به عنوان کانال میانی برای انتشار/اشتراک رویدادها بین سرورها راهاندازی میکند. با این کار هر سروری که پیام دریافت کند، با استفاده از Redis آن را به دیگر سرورها نیز میرساند تا همهٔ کاربران در اتاق مورد نظر پیام را دریافت کنند.
قابلیتها و بهبودهای پیشنهادی
- Delivered/Read receipts: ذخیره وضعیت پیام و ارسال آپدیتها به فرستنده.
- Typing indicators: ارسال رویدادهای typing:start و typing:stop برای بهبود تجربه کاربری.
- Rate limiting: جلوگیری از اسپم با محدودسازی تعداد پیام در واحد زمان.
- File uploads: آپلود مستقیم به S3 یا سرویس مشابه و ذخیره URL در پیام.
- Message ordering: استفاده از timestamp و در صورت نیاز sequence number برای حفظ ترتیب دقیق.
نمونه پیادهسازی typing indicator
// client emits 'typing' with { roomId, typing: true/false }
socket.on('typing', ({ roomId, typing }) => {
socket.to(roomId).emit('typing', { userId: socket.user.id, typing });
});این روش ساده کاربر را در اتاق مطلع میکند که شخصی در حال تایپ است. برای کاهش نویز شبکه میتوان از debounce در کلاینت استفاده کرد.
نکات امنیتی و عملیاتی
- پردازش ورودیها: هرگز متن یا پارامترها را بدون اعتبارسنجی وارد دیتابیس نکنید.
- TLS/HTTPS: ارتباط WebSocket را فقط از طریق TLS برقرار کنید.
- محدودسازی اندازه پیام: جلوگیری از ارسال پیامهای بسیار بزرگ.
- احراز هویت و مجوزها: بررسی کنید کاربر اجازهٔ ورود به یک اتاق را دارد (private rooms).
- نظارت و لاگینگ: متریکها (latency, connections) و خطاها را جمعآوری کنید.
مقایسه WebSocket خام vs Socket.IO
| ویژگی | WebSocket | Socket.IO |
|---|---|---|
| پیروی از پروتکل | RFC WebSocket | بنیان WebSocket + fallback و امکانات بیشتر |
| قابلیتها | ساده و سبک | Rooms، namespaces، reconnection و middleware |
| مقیاسپذیری | نیاز به پیادهسازی pub/sub | Redis adapter موجود |
نکات حرفهای و تجربیات عملی
- برای اتاقهایی با حجم پیام بسیار بالا (مثلاً چت عمومی) از محدودیتهای نرخ و shard کردن استفاده کنید.
- بهجای ارسال پیام کامل برای هر کاربر، در برخی موارد میتوان تنها متادیتا ارسال کرد و محتوای کامل را از CDN یا ذخیرهسازی جداگانه بارگذاری کرد.
- از feature flag برای فعال/غیرفعال کردن قابلیتهای سنگین روی سرورها استفاده کنید.
- در تست بار (load testing)، سناریوهای واقعی مثل بازگشت اتصال، قطع و وصل مکرر و ارسال فایل را شبیهسازی کنید.
جمعبندی
ساخت سرور چت گروهی در Node.js ترکیبی از طراحی بلادرنگ، ذخیرهسازی قابل اعتماد، و معماری مقیاسپذیر است. شروع با Socket.IO و MongoDB مناسب است؛ اما برای تولیدیسازی باید به احراز هویت، امنیت، و استفاده از Redis برای مقیاسپذیری توجه ویژهای داشته باشید. با بهکارگیری الگوهای درست و ابزارهای مناسب میتوان سیستمی پایدار و پاسخگو پیادهسازی کرد.
آیا این مطلب برای شما مفید بود ؟




