Hoàn thành bài "Quản lý vòng đời kết nối, lỗi và nhịp tim" trước để mở khoá bài này.Đến bài trước

Giới thiệu

Trong một ứng dụng thời gian thực, bạn hiếm khi làm việc với WebSockets ở chế độ độc lập. Các client cần các tin nhắn nhắm mục tiêu (gửi tới một socket cụ thể) cũng như các sự kiện phát (broadcast) tới mọi client được kết nối. Trên hết, hầu hết các sản phẩm thực tế vẫn expose một REST API — ví dụ, khi người dùng gửi một biểu mẫu qua HTTP, bạn muốn rằng sự thay đổi đó xuất hiện ngay lập tức trong mọi phiên WebSocket đang mở. Bài học này đi đúng vào vấn đề đó: cách gửi và phát các sự kiện từ máy chủ Node.js, và cách kết nối một REST endpoint với lớp WebSocket của bạn để cả hai bề mặt đồng bộ với nhau.

sơ đồ kiến trúc tổng thể gồm REST client, REST endpoint, máy chủ WebSocket và nhiều WebSocket client, với các mũi tên hai chiều

Gửi tin nhắn tới một client so với phát broadcast

Mỗi socket được kết nối có một id duy nhất được gán bởi máy chủ. Bạn có thể dùng id đó để gửi tin nhắn riêng tư:

javascript
io.to(socket.id).emit("private:message", { text: "only you see this" });

Nếu bạn muốn mọi client được kết nối nhận cùng một payload, bạn phát broadcast trên namespace gốc — không cần đối số target:

javascript
io.emit("announcement", { text: "Server restarts in 5 minutes" });

Còn có `socket.broadcast.emit`, thứ gửi tới mọi người trừ chính socket kích hoạt sự kiện. Điều này hữu ích trong các phòng trò chuyện nơi bạn không muốn phát echo tin nhắn trở lại cho người gửi.

Kết nối REST endpoint với các sự kiện WebSocket

Mẫu chung là: trình xử lý REST thực hiện công việc của nó (ghi vào cơ sở dữ liệu, xác thực đầu vào, v.v.), sau đó yêu cầu lớp WebSocket đẩy một cập nhật. Dưới đây là một ví dụ tối giản dùng Express cùng với Socket.IO:

javascript
const express = require("express");
const http = require("http");
const { Server } = require("socket.io");
const app = express();
app.use(express.json());
const server = http.createServer(app);
const io = new Server(server);
app.post("/orders", (req, res) => {
const order = { id: Date.now(), item: req.body.item, status: "created" };
io.emit("order:new", order);
res.status(201).json(order);
});
io.on("connection", (socket) => {
console.log("client connected:", socket.id);
});
server.listen(3000);

Máy khách HTTP nhận đơn hàng được tạo trong REST response của nó, và mọi tab trình duyệt kết nối qua WebSocket nhận sự kiện `order:new` cùng lúc. Một lần ghi, hai bề mặt.

sơ đồ luồng 4 bước từ REST POST đến cơ sở dữ liệu, phát sự kiện broadcast và phản hồi về client

Gắn mục tiêu tới các phòng (rooms) cụ thể

Khi bạn cần kiểm soát tinh vi hơn "toàn bộ" hay "một socket" thì hãy dùng rooms. Một room là một kênh có tên mà các socket tham gia hoặc rời bỏ:

javascript
io.on("connection", (socket) => {
socket.join("orders");
socket.to("orders").emit("order:new", order);
});

`socket.to("orders")` tới mọi socket trong room đó ngoại trừ người gửi. `io.to("orders").emit(...)` tới mọi người trong room kể cả người gửi. Rooms là cách chuẩn để triển khai các feed theo người dùng, theo đội, hoặc theo chủ đề mà không viết logic định tuyến tùy chỉnh.

Những sai lầm phổ biến

Gọi `io.emit` bên trong handler REST là được, nhưng đừng quên rằng phản hồi REST và phát WebSocket là độc lập — nếu lớp WebSocket gặp sự cố, yêu cầu HTTP vẫn trả về thành công. Đối với các luồng quan trọng, bọc broadcast trong try/catch và log lỗi. Một lỗi khác thường gặp là trộn `socket.emit` (gửi chỉ tới socket hiện tại) với `io.emit` (gửi toàn cầu) và giả sử chúng cùng hoạt động như nhau. Chọn cái phù hợp với ý định của bạn. Cuối cùng, tuyệt đối không tin vào `socket.id` được gửi từ client — luôn tra cứu tại máy chủ từ phiên xác thực trước khi gửi dữ liệu riêng tư.

Tóm tắt

Bạn có ba chế độ phân phối: `socket.emit` cho client hiện tại, `io.emit` cho phát sóng toàn cầu, và rooms cho nhóm mục tiêu. Ghép một handler REST với `io.emit` (hoặc `io.to(room).emit`) là mẫu cầu nối chuẩn để giữ các bề mặt HTTP và WebSocket nhất quán. Giữ broadcast bên trong handler REST sau logics kinh doanh chính, xử lý lỗi WebSocket một cách linh hoạt, và luôn xác thực danh tính ở phía máy chủ trước khi gửi payload riêng tư.

Luyện tập sau buổi học

Bài kiểm tra cuối bài

Ôn lại kiến thức, thử sức và nhận góp ý cho bài làm.

0/1
Đã trả lời · 0/1
Phát broadcast sự kiện và tích hợp với REST API