Socket.io Realtime Chat App: Rooms, Broadcasting và Scale Với Redis Cho Dự Án Việt

5 tháng trước · 11 phút đọc
Socket.io là gì và tại sao dùng cho dự án Việt?
Bạn có bao giờ build một tính năng chat rồi phải tự hỏi: "Tại sao polling mỗi 2 giây mà vẫn delay 3 giây?" Đó là lúc bạn cần WebSocket - và Socket.io là cách nhanh nhất để làm điều đó mà không cần tự xử lý hàng chục edge case.
Socket.io là thư viện JavaScript cho phép giao tiếp bidirectional, realtime giữa client và server. Thay vì client phải hỏi server liên tục (polling), server có thể chủ động đẩy data xuống ngay khi có sự kiện. Phiên bản stable hiện tại là v4.8.1, đã ổn định và được dùng rộng rãi trong production.
WebSocket giữ kết nối liên tục - khác hoàn toàn với HTTP request/response thông thường
Điểm hay là Socket.io không chỉ dùng WebSocket. Nếu mạng của người dùng chặn WebSocket (một số mạng nội bộ doanh nghiệp ở Việt Nam vẫn làm vậy), thư viện tự động fallback sang HTTP long-polling. Người dùng không biết gì, app vẫn chạy.
Các use case phổ biến trong dự án Việt:
- E-learning: chat giữa học viên theo phòng học, thông báo realtime khi giảng viên reply
- Hỗ trợ khách hàng: live chat multi-tenant cho nhiều shop trên cùng một platform
- Collaborative tools: nhiều người cùng edit, xem trạng thái online
Socket.io giúp tiết kiệm đáng kể thời gian phát triển so với tự build WebSocket server từ đầu, nhờ ecosystem adapter sẵn (Redis, MongoDB, Postgres) và API đơn giản.
Setup server: Rooms và namespaces cho multi-tenant
Mình sẽ đi thẳng vào code. Project structure dùng Node.js + Express, không cần framework phức tạp ở bước này.
Namespaces là cấp độ tách biệt cao nhất - như các "kênh" riêng, mỗi namespace có event handler độc lập. Rooms là nhóm con bên trong namespace, client có thể join/leave tự do.
Ví dụ cho platform e-learning: mỗi khóa học là một namespace, mỗi buổi học là một room.
Namespace /chat chứa nhiều rooms - tách biệt hoàn toàn với namespace khác
Broadcasting patterns cần nhớ:
io.emit('event', data)- gửi cho tất cả client đang kết nốisocket.broadcast.emit('event', data)- gửi cho tất cả trừ người gửiio.to('roomId').emit('event', data)- gửi cho tất cả trong roomsocket.to('roomId').emit('event', data)- gửi room nhưng trừ người gửi
Pattern cuối dùng nhiều nhất trong chat app - bạn không muốn message của mình xuất hiện hai lần (đã render optimistically ở client rồi).
JWT authentication với Socket.io middleware
Chat app mà không có auth thì ai cũng vào được. Vấn đề ở đâu? Client kết nối Socket.io trước khi bạn kịp verify họ là ai.
Socket.io có middleware system cho phép chặn kết nối ngay tại handshake - trước khi connection event fire. Đây là chỗ verify JWT.
Token verify tại handshake - kết nối bị từ chối trước khi vào server
Phía client React gửi token khi khởi tạo kết nối:
Một lưu ý thực tế: JWT token có expiry. Bạn cần handle trường hợp token hết hạn khi người dùng đang chat. Pattern phổ biến là dùng auth option kết hợp với refresh token flow - khi connect_error do token expired, gọi refresh endpoint rồi update socket.auth.token và socket.connect() lại.
Tích hợp React + Next.js App Router
Đây là phần hay gây nhầm lẫn nhất. Next.js App Router chạy nhiều thứ trên server bằng default, nhưng Socket.io client lại cần window - chỉ có ở browser.
Cách 1: Custom server (recommended cho production)
Với Next.js, cách clean nhất là tách Socket.io server ra file riêng, chạy song song:
Cách 2: Custom hook cho React client
Custom hook encapsulate toàn bộ socket logic - component chỉ cần gọi useSocket
Nếu bạn mới làm quen React, khóa Xây Dựng Website với ReactJS cover đủ hooks và component pattern để hiểu đoạn code trên. Còn nếu muốn đi sâu vào Node.js phía server, khóa Node & ExpressJS cũng rất hữu ích.
Scale với Redis adapter và deploy production
Local chạy một process thì Socket.io hoạt động ngon. Vấn đề xuất hiện khi bạn scale lên 2-3 server instance: client A kết nối server 1, client B kết nối server 2, message A gửi vào room không đến được B vì hai server không biết nhau.
Redis adapter giải quyết bằng cách dùng Redis pub/sub làm message broker giữa các instances.
Redis làm cầu nối giữa các server - message từ instance 1 broadcast được tới client ở instance 2
Sau khi có Redis adapter, toàn bộ io.to('room').emit() tự động sync qua tất cả instances mà không cần thay đổi code logic.
Deploy thực tế cho dự án Việt:
Vercel serverless không support persistent WebSocket connection. Options thực tế:
- Render.com - free tier, support Node.js server, deploy dễ với
render.yaml - Railway - $5/tháng, tương tự Render nhưng UI cleaner
- VPS DigitalOcean/Vultr - tự manage, $6/tháng, dùng PM2 + Nginx
Nếu bạn nhất quyết dùng Vercel cho Next.js frontend, deploy Socket.io server riêng trên Render/Railway rồi point NEXT_PUBLIC_SOCKET_URL vào đó.
Fallback polling tự động hoạt động với config mặc định của Socket.io - không cần làm gì thêm. Nhưng nếu muốn tối ưu:
Takeaways:
- Một server instance thì không cần Redis - đừng over-engineer
- Redis adapter dùng version
@socket.io/redis-adapter@^8.xvới Socket.io v4.x - Upstash Redis free tier đủ dùng cho MVP - 10,000 requests per day (dev.to article: From Zero to Cached) request/ngày
- Monitor
io.engine.clientsCountđể biết khi nào cần scale thêm
Checklist và bước tiếp theo
Mình đã build đủ prototype chat app theo kiểu này cho vài dự án - phần hay bị bỏ quên nhất không phải technical mà là rate limiting và abuse prevention.
Một user gửi 1000 message/giây là socket.io vẫn forward hết. Thêm rate limit đơn giản bằng middleware:
Checklist build chat app production-ready từ dev đến deploy
Bước tiếp theo nếu muốn đưa lên production:
- Setup server: Node.js + Express + Socket.io v4.x, namespace
/chat - Auth middleware: verify JWT tại handshake, gắn
socket.data.userId - Rooms logic:
join_room,send_message,leave_roomevents - React client: custom hook
useSocketvới cleanup tronguseEffect - Redis adapter: thêm khi cần scale lên 2+ instances (đừng làm sớm)
- Rate limiting: tối thiểu 60 msg/phút/user
- Deploy: Render/Railway cho socket server, Vercel cho Next.js frontend
Download checklist đầy đủ tại đây: Tải checklist Socket.io Chat App
Gặp vướng mắc phần nào, drop comment. Phần hay bị hỏi nhất là CORS config và JWT refresh khi đang chat - mình sẽ reply.
