Интеграция Socket.IO для real-time коммуникации на сайте
Socket.IO — библиотека для двунаправленной коммуникации между сервером и клиентом. В отличие от чистого WebSocket, Socket.IO обеспечивает автоматическое переподключение, fallback на long-polling, комнаты и неймспейсы.
Установка и базовая настройка
npm install socket.io # сервер
npm install socket.io-client # клиент
Сервер (Node.js + Express)
import { createServer } from 'http';
import { Server } from 'socket.io';
import express from 'express';
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: process.env.CLIENT_URL,
credentials: true
},
pingTimeout: 60000,
pingInterval: 25000
});
// Middleware аутентификации
io.use(async (socket, next) => {
const token = socket.handshake.auth.token;
try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
socket.data.userId = payload.sub;
socket.data.user = await userRepo.findById(payload.sub);
next();
} catch {
next(new Error('Authentication error'));
}
});
io.on('connection', (socket) => {
const userId = socket.data.userId;
console.log(`User ${userId} connected: ${socket.id}`);
// Присоединить к персональной комнате
socket.join(`user:${userId}`);
// Обработчики событий
socket.on('join:room', async ({ roomId }) => {
const canJoin = await checkRoomAccess(userId, roomId);
if (!canJoin) {
socket.emit('error', { message: 'Access denied' });
return;
}
socket.join(`room:${roomId}`);
socket.to(`room:${roomId}`).emit('user:joined', {
userId, user: socket.data.user
});
});
socket.on('message:send', async ({ roomId, content }) => {
const message = await messageRepo.create({ roomId, userId, content });
io.to(`room:${roomId}`).emit('message:new', message);
});
socket.on('disconnect', () => {
console.log(`User ${userId} disconnected`);
// Уведомить комнаты о выходе
io.emit('user:offline', { userId });
});
});
httpServer.listen(3000);
Клиент (React)
// hooks/useSocket.ts
import { useEffect, useRef } from 'react';
import { io, Socket } from 'socket.io-client';
import { useAuthStore } from '@/stores/auth';
export function useSocket(): Socket | null {
const socketRef = useRef<Socket | null>(null);
const token = useAuthStore(s => s.token);
useEffect(() => {
if (!token) return;
const socket = io(process.env.NEXT_PUBLIC_WS_URL, {
auth: { token },
reconnection: true,
reconnectionDelay: 1000,
reconnectionAttempts: 5,
transports: ['websocket', 'polling']
});
socket.on('connect', () => console.log('Socket connected'));
socket.on('connect_error', (err) => console.error('Connection error:', err));
socketRef.current = socket;
return () => {
socket.disconnect();
socketRef.current = null;
};
}, [token]);
return socketRef.current;
}
// Компонент чата
function ChatRoom({ roomId }) {
const socket = useSocket();
const [messages, setMessages] = useState([]);
useEffect(() => {
if (!socket) return;
socket.emit('join:room', { roomId });
socket.on('message:new', (message) => {
setMessages(prev => [...prev, message]);
});
return () => {
socket.off('message:new');
socket.emit('leave:room', { roomId });
};
}, [socket, roomId]);
const sendMessage = (content: string) => {
socket?.emit('message:send', { roomId, content });
};
return <ChatUI messages={messages} onSend={sendMessage} />;
}
Масштабирование через Redis Adapter
При нескольких Node.js процессах Socket.IO нужен Redis для синхронизации:
import { createAdapter } from '@socket.io/redis-adapter';
import { createClient } from 'redis';
const pubClient = createClient({ url: process.env.REDIS_URL });
const subClient = pubClient.duplicate();
await Promise.all([pubClient.connect(), subClient.connect()]);
io.adapter(createAdapter(pubClient, subClient));
// Теперь io.to('room:123').emit() работает across всех Node.js процессов
Уведомления из других сервисов
// Из бэкенд-сервиса отправить уведомление конкретному пользователю
// через Redis pub/sub (без прямого доступа к Socket.IO серверу)
const publisher = createClient({ url: process.env.REDIS_URL });
await publisher.publish('socket:notify', JSON.stringify({
room: `user:${userId}`,
event: 'order:status_changed',
data: { orderId, newStatus: 'shipped' }
}));
Сроки
Socket.IO сервер + клиентский хук + чат-комнаты + Redis adapter — 1–2 недели.







